與PyInstaller解包全流程)
簡介EXE可執(zhí)行文件解壓工具是一份專門提取EXE內(nèi)部資源的實(shí)用工具包面向開發(fā)者、逆向工程師及需要分析或復(fù)用程序素材的用戶可針對非安裝類可執(zhí)行文件抽取圖片、文本、聲音等嵌入數(shù)據(jù)適用于軟件結(jié)構(gòu)分析、資源漢化與素材復(fù)用場景。壓縮包共167個(gè)文件大小僅5.14MB以UniExtract主程序及配套動(dòng)態(tài)庫為核心同時(shí)含大量txt說明文檔、exe輔助程序、unp解包腳本、doc使用說明與ini配置項(xiàng)目錄結(jié)構(gòu)清晰便于按需調(diào)用。解壓后可直接用于探索EXE文件組成或借助內(nèi)置多種解包引擎處理常見封裝格式快速定位隱藏資源并修改皮膚、修復(fù)數(shù)據(jù)或了解程序?qū)崿F(xiàn)方式。該工具整體輕量易用已有3503人學(xué)習(xí)下載使用時(shí)可結(jié)合工程需要提取素材但需注意遵守版權(quán)法規(guī)與軟件條款避免侵權(quán)用途。1. EXE可執(zhí)行文件解壓工具先搞清你想解出什么再動(dòng)手你手上有一個(gè) EXE雙擊就裝上、裝上就能跑但有一天你需要把它“打開看看”??赡苁窍胩崂锩娴膱D標(biāo)和版本信息可能是想確認(rèn)某個(gè) PyInstaller 打包的工具到底放了哪些模塊也可能是想把安裝包直接還原成一個(gè)免安裝目錄扔到服務(wù)器上。這時(shí)候你會發(fā)現(xiàn)EXE 可執(zhí)行文件解壓工具并不是某個(gè)單一軟件而是一套按形態(tài)選擇工具的解法集合。這個(gè)方向能解決三類實(shí)際問題把自解壓包和安裝器當(dāng)壓縮包拆開、把資源區(qū)里的素材與元數(shù)據(jù)提取出來、把打包器還原成接近源碼的中間產(chǎn)物。適合做軟件供應(yīng)鏈審查的開發(fā)、做應(yīng)用免安裝化的運(yùn)維以及想換圖標(biāo)或做漢化的普通用戶。但有一個(gè)前提要先立住EXE 不是鐵板一塊解壓之前必須先判斷它屬于哪種容器形態(tài)選錯(cuò)工具只會浪費(fèi)時(shí)間。2. EXE內(nèi)部是四種容器先判別形態(tài)再選對工具2.1 PE結(jié)構(gòu)里藏著什么頭部、資源三件套與載荷區(qū)Windows 可執(zhí)行文件的標(biāo)準(zhǔn)格式是 PEPortable Executable文件開頭有 MZ 頭偏移 0x3C 的位置記錄著 PE 頭位置后面跟著節(jié)區(qū)和資源段。對解壓工具來說真正要關(guān)注的是兩部分.rsrc資源段和文件末尾的 overlay 附加數(shù)據(jù)。.rsrc里放的是圖標(biāo)、版本信息、對話框模板和字符串表這部分用 Resource Hacker 這類工具可以直接提。而 overlay 區(qū)才是安裝包和打包器做文章的地方Inno Setup、NSIS、PyInstaller 會把真正的“貨物”整塊追加在 PE 文件尾部PE 頭只負(fù)責(zé)讓系統(tǒng)把這部分當(dāng)成合法程序加載。這也是為什么很多 EXE 一解壓就能看到完整文件列表而另一些只能拆出零散的 DLL 和配置。2.2 四種形態(tài)識別法看圖標(biāo)、看節(jié)區(qū)、看字符串常見做法是先看三樣?xùn)|西圖標(biāo)風(fēng)格、文件體積、特征字符串。PyInstaller 默認(rèn)用一把傘形圖標(biāo)NSIS 安裝包常見藍(lán)色齒輪圖標(biāo)Inno Setup 多為用戶自定義圖標(biāo)——但圖標(biāo)不可靠因?yàn)槿魏未虬鞫贾С謸Q圖標(biāo)所以更要看體積和字符串。例如一個(gè)幾十 MB 的 EXE 放著 Python 相關(guān) DLL 沒壓縮或安裝包內(nèi)部包含app.asar、*.pyc之類文件字符串掃描能直接暴露身份。Windows 下可以用 Sysinternals 的 strings 工具或直接在 PowerShell 里對二進(jìn)制做匹配strings.exe -n 8 target.exe | grep -iE Inno Setup|Nullsoft|PyInstaller|python3.dll|app.asar邏輯說明-n 8要求字符串至少連續(xù) 8 個(gè)可打印字符避免垃圾匹配grep 的大小寫不敏感匹配會覆蓋常見打包器簽名。如果你在純 Windows 環(huán)境沒有 strings.exe用findstr /i Inno Setup PyInstaller target.exe也能湊合但 findstr 對二進(jìn)制文件的中文和寬字符處理不佳能用 strings 就別將就。參數(shù)說明掃到Inno Setup Setup Data是 Inno掃到Nullsoft是 NSIS掃到PyInstaller或python3.dll說明是 Python 系打包接下來按對應(yīng)路線走。掃描結(jié)果為空但不代表是原生編譯還要再看節(jié)區(qū)名。2.3 解壓工具選型對照表容器形態(tài)典型特征首選工具能拿到什么安裝包裝器Inno Setup / NSIS / InstallShieldinnoextract / 7-Zip安裝目錄文件、腳本、插件腳本包裝型bat轉(zhuǎn)exe、SFX自解壓包7-Zip / Resource Hacker原始腳本或批處理內(nèi)容Python 系PyInstaller / Py2exepyinstxtractor pycdcpyc 字節(jié)碼、依賴模塊與靜態(tài)文件原生編譯型C/C / Go / Rust / GraalVM 打包Resource Hacker 提資源反編譯需另走 IDA/Ghidra圖標(biāo)、版本、對話框不含業(yè)務(wù)源碼GraalVM 打包成的 EXE 是純機(jī)器碼解壓工具只能動(dòng)資源區(qū)拿不到任何業(yè)務(wù)邏輯bat 轉(zhuǎn) EXE 工具比如常見的 BatToEXE Converter則相反它把腳本塞進(jìn)外殼硬解后最容易得到.bat 原稿。把形態(tài)認(rèn)清楚后面每一步才有的放矢。3. 通用解壓組合7-Zip 硬解與 Resource Hacker 資源提取3.1 用 7-Zip 硬解自解壓包命令行與 GUI 兩種姿勢7-Zip 是解壓 EXE 的第一選擇它對自解壓包SFX的支持最好。WinRAR 和 7-Zip 自帶的 SFX 模塊本質(zhì)上就是把解壓器頭和壓縮數(shù)據(jù)拼在一個(gè) EXE 里7-Zip 能直接識別嵌入的壓縮流。對于部分 NSIS 包7-Zip 也能把$INSTDIR里的內(nèi)容完整列出來。GUI 操作就一個(gè)動(dòng)作右鍵 EXE選“打開壓縮包”能看到內(nèi)部文件就直接拖出來。命令行更可控寫進(jìn)腳本方便批量處理7z x target.exe -oOUTDIR -y邏輯說明x是提取并保留目錄結(jié)構(gòu)e是把所有文件丟到同一層解壓安裝包必須用x否則插件目錄和子目錄全亂套。-oOUTDIR指定輸出目錄注意-o后面不能有空格。-y表示遇到覆蓋詢問全部選是適合流水線反復(fù)跑。參數(shù)說明如果只想要里面某個(gè)子目錄可以追加-ir!plugins/*這類包含規(guī)則反之用-xr!*.pdb排除調(diào)試符號。7-Zip 硬解對 Inno Setup 新版和帶加密的 NSIS 包可能只列出一部分文件這時(shí)候別死磕換定向工具。3.2 Resource Hacker提取圖標(biāo)、版本信息和對話框資源資源提取的正主是 Resource Hacker 這類工具。它能直接讀 PE 的資源段把圖標(biāo)、位圖、版本號、Manifest、對話框模板一項(xiàng)項(xiàng)列出來也可以導(dǎo)出或替換。對只想換圖標(biāo)、看版本、做漢化的場景它比任何通用解壓器都好用。命令行提取所有圖標(biāo)ResourceHacker.exe -open target.exe -save icons -action extract -mask ICON,, -log log.txt邏輯說明-open指定目標(biāo) EXE-action extract表示提取-mask ICON,,匹配所有圖標(biāo)資源類型 ICON、名稱不限、語言不限-save icons把結(jié)果保存到 icons 目錄。-log記錄詳細(xì)日志批量跑的時(shí)候方便排查哪個(gè)文件沒提出來。參數(shù)說明類似 mask 寫法可以換成VERSION拿版本信息、DIALOG拿對話框模板。GUI 模式更直觀左側(cè)樹展開后右鍵選導(dǎo)出資源即可只是沒法批量。實(shí)際操作中我一般先命令行列清單再針對個(gè)別資源用 GUI 微調(diào)。3.3 換圖標(biāo)與資源修改簽名失效和必須先備份的問題用 Resource Hacker 替換圖標(biāo)也是常見需求命令是ResourceHacker.exe -open target.exe -save new.exe -action addoverwrite -res app.ico -mask ICON,MAIN,邏輯說明addoverwrite把a(bǔ)pp.ico寫到目標(biāo) EXE 的ICON,MAIN,資源位。這里MAIN是主圖標(biāo)組的名字很多程序默認(rèn)叫MAIN但也有人改成別的先提取后再回填最穩(wěn)妥。參數(shù)說明-res可以反復(fù)傳一次替換多個(gè)資源。做完這一步有個(gè)大坑一旦改動(dòng) EXE 內(nèi)容原文件的數(shù)字簽名立刻失效。Windows 會提示“發(fā)布者未知”殺毒軟件也可能提高警惕。所以改任何資源前先把原始 EXE 留一份備份不要直接在原文件上做覆蓋保存。4. 定向拆解安裝包與自解壓殼Inno Setup、NSIS 與 Squirrel 的還原路徑4.1 Inno Setup 包識別與 innoextract 常用參數(shù)遇到 Inno Setup 包7-Zip 可能能列出部分文件但腳本、自定義消息、磁盤布局信息都會被跳過。開源工具 innoextract 專門處理 Inno Setup 的結(jié)構(gòu)能按安裝腳本的原始目錄還原文件。先確認(rèn)特征串再?zèng)Q定是否用它確認(rèn)方法就是第 2 章里的 strings 掃描看到Inno Setup Setup Data字樣就可以上。innoextract -l setup.exe innoextract -e -d outdir setup.exe邏輯說明第一條命令-l只列出內(nèi)部文件清單不實(shí)際解壓適合先確認(rèn)包里是不是你要的東西。第二條-e執(zhí)行解壓-d outdir指定輸出目錄。innoextract 對 Inno Setup 5.x 支持最完整6.x 部分版本需要額外參數(shù)處理。參數(shù)說明如果只想解指定組件用-I component_name按名稱過濾配合-x排除多余內(nèi)容。若安裝包被作者設(shè)置了密碼innoextract 會提示并提供--password選項(xiàng)但如果你沒有授權(quán)密碼別硬來。4.2 NSIS 解壓與 Squirrel/Electron 包的接續(xù)處理NSIS 包的識別特征是Nullsoft字符串7-Zip 對大多數(shù) NSIS 包可以直接硬解命令很簡單7z x nsis-installer.exe -oextracted -y邏輯說明NSIS 安裝包把文件區(qū)以自定義格式附加在 PE 尾部7-Zip 能解析其中大部分壓縮塊。解壓后通常會看到$INSTDIR、$PLUGINSDIR這類目錄名前者是安裝目標(biāo)目錄后者是插件臨時(shí)目錄。$PLUGINSDIR里那些 DLL 是 NSIS 插件別當(dāng)垃圾刪有些是程序運(yùn)行必需的。參數(shù)說明如果 7-Zip 解到一半報(bào)錯(cuò)說明 NSIS 用了非標(biāo)準(zhǔn)壓縮方式可以改用 nsisunz 這類專用工具讀出文件偏移再手工剝離。還有一類 Squirrel 安裝器常見于 Electron 應(yīng)用內(nèi)部不是 MSI而是把a(bǔ)pp-64.7z嵌在 EXE 里7-Zip 解出后再用 asar 工具拆resources/app.asar那是另一個(gè)分支的活兒但方向比硬解 EXE 正確得多。4.3 自解壓 SFX 的通用還原思路自解壓包是所有 EXE 里最好對付的因?yàn)樗谋举|(zhì)就是“解壓器 壓縮數(shù)據(jù)”。WinRAR 做的 SFX 用 WinRAR 打開直接能看7-Zip 做的 SFX 用 7-Zip 打開同樣可以。遇到不認(rèn)識的自解壓殼先看文件尾部有沒有壓縮格式的魔數(shù)常見的有 Rar、7z、Zip 頭然后用對應(yīng)解壓器指定從偏移處提取。另一種通用思路是把 EXE 當(dāng)作普通文件搜內(nèi)嵌壓縮流。用 binwalk 掃描文件內(nèi)的壓縮簽名找到偏移后直接用dd切出壓縮塊再解壓binwalk target.exe | grep -iE rar|7z|zip dd iftarget.exe ofembedded.7z bs1 skip102345 count456789邏輯說明binwalk 輸出的是各壓縮流的起始偏移dd 按偏移和長度切出原始壓縮數(shù)據(jù)。這個(gè)方法看著原始但對付加了自定義殼的自解壓程序很有效因?yàn)樗@開了外殼直接拿內(nèi)層數(shù)據(jù)。參數(shù)說明bs1保證按字節(jié)切割skip 和 count 的單位取決于 binwalk 輸出的偏移單位常見的是 10 進(jìn)制字節(jié)偏移。切出來的文件用對應(yīng)解壓器打開基本就是安裝目錄的全部內(nèi)容。5. PyInstaller 系 EXE 解包與避坑清單解出來也不一定跑得起來5.1 用 pyinstxtractor 把 PyInstaller 包拆成原始目錄Python 轉(zhuǎn) EXE 的生態(tài)里 PyInstaller 占比最高它打包出的 EXE 自帶 Python 運(yùn)行時(shí)和模塊歸檔。解包工具首選開源腳本 pyinstxtractor.py把它和目標(biāo) EXE 放在同一目錄執(zhí)行python pyinstxtractor.py target.exe邏輯說明腳本會讀取 PyInstaller 的歸檔結(jié)構(gòu)把嵌入的模塊、數(shù)據(jù)文件、依賴 DLL 全部導(dǎo)出到一個(gè)target.exe_extracted目錄。主程序入口通常是main.pyc或與你打包時(shí)的入口腳本同名的.pyc大量第三方庫則被打進(jìn)PYZ-00.pyz需要用腳本再拆一層python pyinstxtractor.py target.exe_extracted/PYZ-00.pyz參數(shù)說明腳本不需要額外參數(shù)但要求本機(jī)有 Python 3.6 以上環(huán)境且不能用打包器內(nèi)置的 Python。拆完的目錄里如果出現(xiàn)大量.pyd和.dll別意外那正是運(yùn)行時(shí)依賴。如果你發(fā)現(xiàn)提取出來的 pyc 文件大小都是 0 或明顯異常先確認(rèn) EXE 是否被加殼而不是腳本沒跑對。5.2 pyc 反編譯與 Python 版本適配拆出.pyc只完成了一半要看代碼邏輯還得反編譯。工具選擇看 Python 版本3.7 及以下用 uncompyle6 比較成熟3.8 以上 uncompyle6 基本翻車推薦用 pycdc。兩個(gè)命令分別長這樣uncompyle6 target.exe_extracted/main.pyc main.py pycdc target.exe_extracted/PYZ-00.pyz_extracted/pkg/module.pyc module.py邏輯說明uncompyle6 輸出可讀性高但遇到新字節(jié)碼就報(bào)TypeError或直接失敗。pycdc 是 C 實(shí)現(xiàn)容錯(cuò)強(qiáng)但生成的代碼偶爾在控制流上不完美注釋和類型信息也會丟。兩條命令的輸出都建議馬上落盤不要直接打屏因?yàn)榉淳幾g結(jié)果可能很長。參數(shù)說明如果 pyc 文件頭部不是標(biāo)準(zhǔn) Python 魔數(shù)說明打包時(shí)開了--key加密。pyinstxtractor 會把這類文件標(biāo)記出來直接反編譯只會得到亂碼。正規(guī)做法是先確認(rèn)你對這個(gè)程序有無授權(quán)拿到密鑰后再處理不要用這些工具做繞過授權(quán)的事。5.3 五條必看踩坑記錄坑一解出來沒有 .pyc只有一堆 .dll 和 .pyd現(xiàn)象用 7-Zip 硬解 PyInstaller 包文件列表里根本看不到.pyc。 原因PyInstaller 把所有模塊壓縮在 overlay 區(qū)的自定義歸檔里7-Zip 只解析到資源段和部分附件。 解決改用 pyinstxtractor.py 定向解包它會按 PyInstaller 歸檔格式切出所有模塊緩存??佣ncompyle6 反編譯報(bào)版本不支持現(xiàn)象Python 3.8 打包的 EXEuncompyle6 一跑就報(bào)錯(cuò)。 原因字節(jié)碼版本不在 uncompyle6 支持范圍內(nèi)。 解決換 pycdc或者在反編譯前用file main.pyc確認(rèn)源碼字節(jié)碼版本再選匹配工具??尤绦蛱崾尽爸付ǖ目蓤?zhí)行文件不是此操作系統(tǒng)平臺的有效應(yīng)用程序”現(xiàn)象解包出的 EXE 在另一臺機(jī)器上運(yùn)行直接報(bào)平臺錯(cuò)誤。 原因打包時(shí)目標(biāo)架構(gòu)和運(yùn)行平臺不匹配比如 32 位包跑在純 64 位系統(tǒng)上或 ARM64 包跑在 x64 上。 解決解包前先確認(rèn)機(jī)器碼架構(gòu)。用 PowerShell 讀 PE 頭的 Machine 字段最快$fs [System.IO.File]::ReadAllBytes(target.exe) $peOffset [BitConverter]::ToInt32($fs, 0x3C) [BitConverter]::ToUInt16($fs, $peOffset 4)邏輯說明PE 頭偏移記錄在文件 0x3C 位置Machine 字段在 PE 簽名后兩個(gè)字節(jié)。輸出0x8664是 x640x014C是 x860xAA64是 ARM64據(jù)此選對版本的包再解壓。參數(shù)說明這個(gè)技巧對所有 EXE 都通用不限于 PyInstaller 產(chǎn)物。遇到平臺報(bào)錯(cuò)先查這里別急著換解壓工具??铀慕鈮汉竽夸浲暾绦騿?dòng)崩潰或缺 DLL現(xiàn)象文件全在一運(yùn)行就報(bào)缺少VCRUNTIME140.dll或python3.dll。 原因PyInstaller 單文件模式運(yùn)行時(shí)會把依賴釋放到臨時(shí)目錄解壓出來的目錄缺少運(yùn)行時(shí)組件。 解決把target.exe_extracted里所有 DLL 保留不要按文件名“清理”看似多余的文件同時(shí)確認(rèn)系統(tǒng)裝了對應(yīng)的 VC Redistributable??游褰獬龅奈募葘?shí)際運(yùn)行時(shí)少一半現(xiàn)象包內(nèi)文件列表明顯少于程序?qū)嶋H寫出的文件。 原因onefile 模式在運(yùn)行時(shí)動(dòng)態(tài)釋放文件有些資源是首啟后才生成的。 解決用 Process Monitor 監(jiān)控原程序啟動(dòng)過程過濾 EXE 進(jìn)程名看它訪問并寫入了哪些路徑把這些內(nèi)容補(bǔ)進(jìn)你的解壓目錄。6. 從解壓到免安裝化驗(yàn)證完整性與補(bǔ)齊運(yùn)行庫的收尾動(dòng)作6.1 用文件對比和 ProcMon 驗(yàn)證解壓產(chǎn)物是否完整解壓完先別急著刪安裝包驗(yàn)證完整性最省事的方式是做目錄差異對比。假設(shè)你在一臺干凈機(jī)器上先記錄安裝前目錄再用正常方式安裝一遍最后和解壓產(chǎn)物對比robocopy C:\installed_dir C:\extracted_dir /MIR /L邏輯說明/MIR鏡像目錄/L只列出差異不執(zhí)行復(fù)制。輸出里會顯示哪些文件安裝版有、解壓版沒有漏掉的文件逐個(gè)補(bǔ)。如果差異集中在注冊表相關(guān)路徑說明程序安裝時(shí)把配置寫到別處這時(shí)候用 Process Monitor 抓一次運(yùn)行記錄。參數(shù)說明/L是只讀試跑安全確認(rèn)無誤后去掉/L正式同步。ProcMon 過濾方式設(shè)置 Process Name 為你的 EXE 名再觀察 RegSetValue 和 CreateFile 操作就能定位它寫了哪些注冊表項(xiàng)和配置文件。6.2 運(yùn)行庫判斷與補(bǔ)依賴清單免安裝版跑不起來的另一個(gè)常見原因是缺運(yùn)行庫。判斷 EXE 依賴哪種運(yùn)行庫看導(dǎo)入表最直接dumpbin /imports target.exe | findstr /i Qt5Core msvcp VCRUNTIME MSCOREE邏輯說明Qt5Core說明是 Qt 程序需要帶plugins目錄MSCOREE.dll說明是 .NET需要對應(yīng) .NET RuntimeVCRUNTIME是 VC 運(yùn)行庫。只靠系統(tǒng)裝庫不一定對因?yàn)榻鈮耗夸浫绻绷?DLL 鄰居程序照樣找不到依賴。解決思路把原安裝目錄里與 EXE 同級的 DLL 全部保留Qt 程序把platforms文件夾也拷上.NET 程序優(yōu)先用 ILSpy 看入口邏輯確認(rèn)目標(biāo)框架版本再配運(yùn)行時(shí)。6.3 解壓不等于反編譯一條清晰的驗(yàn)證鏈路最后說一條重要的邊界解壓工具解決的是“拿到文件和資源”不是“看懂業(yè)務(wù)邏輯”。原生編譯型程序解壓完仍然是機(jī)器碼要分析邏輯得進(jìn)反匯編或反編譯工具鏈這時(shí)你手里的解壓產(chǎn)物是素材不是答案。我現(xiàn)在的固定習(xí)慣是先 strings 和 PE 頭判斷形態(tài)再按形態(tài)選解壓工具最后用文件對比和 ProcMon 驗(yàn)證驗(yàn)證不過就回頭查依賴。這套流程跑過很多次救回過不少被誤刪“無用文件”的翻車現(xiàn)場。資源提取時(shí)永遠(yuǎn)留原始備份改過一次圖標(biāo)就知道簽名失效有多麻煩。希望幫到你這一套下來至少不會再對著一個(gè)打不開的 EXE 干瞪眼。本文還有配套的精品資源點(diǎn)擊獲取