戰(zhàn):逆向工程與固件分析指南)
簡(jiǎn)介IDA5.0 是一款經(jīng)典的反匯編與逆向分析工具本資源面向逆向工程初學(xué)者、安全研究人員及需要分析二進(jìn)制程序的開(kāi)發(fā)者幫助解決可執(zhí)行文件反匯編、靜態(tài)分析與代碼結(jié)構(gòu)還原等需求。壓縮包內(nèi)共114個(gè)文件約55.71MB以pat插件文件、exe可執(zhí)行程序、idt與sig簽名庫(kù)、bat批處理腳本為主另含idc腳本、dll動(dòng)態(tài)庫(kù)、readme說(shuō)明文檔及少量源碼與本地化文件覆蓋工具運(yùn)行、插件擴(kuò)展與簽名識(shí)別等用途。目前已有320人學(xué)習(xí)下載說(shuō)明其在逆向入門(mén)與老版本工具研究場(chǎng)景中仍具參考價(jià)值。通過(guò)該資源讀者可獲得一套可直接部署的IDA5.0工作環(huán)境借助簽名庫(kù)與插件提升函數(shù)識(shí)別效率并利用批處理與腳本文件快速完成啟動(dòng)配置適合用于惡意樣本分析、漏洞研究及軟件逆向?qū)W習(xí)的早期實(shí)踐。1. 反匯編工具 IDA5.0為什么老版本反而成了逆向工程的“活化石”如果你在 2024 年還聽(tīng)到有人用 IDA5.0 做逆向分析第一反應(yīng)可能是“這人是不是沒(méi)網(wǎng)盤(pán)”。但實(shí)際情況是IDA5.0 作為一款經(jīng)典的反匯編工具至今仍活躍在嵌入式固件分析、老舊工控設(shè)備逆向、CTF 逆向題復(fù)現(xiàn)以及部分惡意樣本的靜態(tài)分析場(chǎng)景里。原因很直接它體積小、啟動(dòng)快、對(duì) 32 位 x86 和早期 ARM 指令集支持穩(wěn)定而且大量歷史遺留的 .idb 數(shù)據(jù)庫(kù)文件就是用這個(gè)版本生成的。你拿新版 IDA 去打開(kāi)有時(shí)反而會(huì)因?yàn)閿?shù)據(jù)庫(kù)格式升級(jí)而丟失注釋或函數(shù)命名。所以“反匯編工具 IDA5.0”這個(gè)組合不是懷舊而是一個(gè)真實(shí)存在的工程需求——你需要一個(gè)能跑在老系統(tǒng)上、能讀舊數(shù)據(jù)庫(kù)、能快速驗(yàn)證想法的輕量級(jí)反匯編環(huán)境。這篇文章面向的是手里有固件、有樣本、有 CTF 題目但不想被新版授權(quán)和龐大安裝包折騰的從業(yè)者。我會(huì)把 IDA5.0 的獲取、安裝、核心操作、腳本擴(kuò)展和常見(jiàn)翻車(chē)點(diǎn)拆開(kāi)講讓你能直接復(fù)現(xiàn)一套可用的分析流程。2. 把 IDA5.0 跑起來(lái)安裝、配置與第一個(gè)反匯編窗口2.1 為什么選 IDA5.0 而不是新版三個(gè)硬理由第一個(gè)理由是數(shù)據(jù)庫(kù)兼容性。IDA 的 .idb 文件在 5.x 到 6.x 之間有過(guò)一次內(nèi)部格式調(diào)整雖然新版能向上兼容但反過(guò)來(lái)不行。如果你手頭有一批 2005 到 2010 年間做的逆向項(xiàng)目用 IDA5.0 打開(kāi)是最穩(wěn)妥的。第二個(gè)理由是運(yùn)行環(huán)境。IDA5.0 的安裝包只有幾十兆在 Windows XP、Windows 7 甚至 Wine 下都能跑而新版 IDA 對(duì) .NET 版本和顯卡驅(qū)動(dòng)有要求。第三個(gè)理由是腳本生態(tài)。IDC 腳本在 5.0 里已經(jīng)非常成熟很多老外掛和批量分析腳本都是基于這個(gè)版本的 API 寫(xiě)的你直接拿來(lái)改改就能用。提示如果你只是分析 64 位現(xiàn)代樣本直接上新版 IDA 或 Ghidra不要用 5.0 硬扛。IDA5.0 的強(qiáng)項(xiàng)是 32 位和 16 位代碼。安裝過(guò)程本身不復(fù)雜但有幾個(gè)細(xì)節(jié)容易翻車(chē)。常見(jiàn)做法是從可信的逆向工程資源站下載 IDA5.0 的壓縮包解壓到不含中文和空格的路徑比如C:\ida50。然后運(yùn)行ida.exe第一次啟動(dòng)會(huì)提示你選擇許可證類型。如果你手里沒(méi)有正版授權(quán)社區(qū)里流傳的免費(fèi)版通常只支持 PE 和 ELF 的 32 位反匯編這對(duì)大多數(shù)固件分析已經(jīng)夠用。# 假設(shè)你解壓到了 C:\ida50 cd C:\ida50 # 查看目錄結(jié)構(gòu)確認(rèn) ida.exe 和 idc 目錄存在 dir # 啟動(dòng) IDA 5.0 ida.exe這段命令只是確認(rèn)安裝完整性。ida.exe是主程序idc目錄下是 IDC 腳本的示例和頭文件。如果你在 Linux 下用 Wine把ida.exe換成wine ida.exe即可。參數(shù)方面IDA5.0 沒(méi)有太多啟動(dòng)參數(shù)但你可以用-A進(jìn)入自動(dòng)分析模式用-S指定腳本文件。比如ida.exe -A -Sanalyze.idc target.bin可以在打開(kāi)文件后自動(dòng)跑一遍分析腳本。2.2 加載第一個(gè)二進(jìn)制文件從 PE 到裸二進(jìn)制打開(kāi) IDA5.0 后你會(huì)看到歡迎界面。點(diǎn)擊 “New” 選擇要分析的文件。如果是 Windows PE 文件IDA 會(huì)自動(dòng)識(shí)別格式并加載。如果是嵌入式固件通常是裸二進(jìn)制你需要手動(dòng)指定加載地址和處理器類型。這一步是新手最容易卡住的地方。假設(shè)你有一個(gè) ARM 架構(gòu)的固件firmware.bin加載地址是0x08000000。操作步驟是File - Open - 選擇firmware.bin- 在彈出的 “Load a new file” 對(duì)話框中Processor type 選擇 “ARM”然后點(diǎn)擊 “Set” 按鈕在 “ROM start address” 和 “Loading address” 都填0x08000000。接著點(diǎn)擊 OKIDA 會(huì)開(kāi)始反匯編。# 這是一個(gè) IDC 腳本示例用于在加載后自動(dòng)標(biāo)記函數(shù) # 保存為 auto_mark.idc然后在 IDA 中 File - Script file 運(yùn)行 #include idc.idc static main() { auto addr; auto count 0; // 從 0x08000000 開(kāi)始遍歷尋找函數(shù)序言 for (addr 0x08000000; addr 0x08100000; addr addr 4) { // 檢查是否是常見(jiàn)的 ARM 函數(shù)開(kāi)頭push {..., lr} if (GetMnem(addr) PUSH strstr(GetDisasm(addr), LR) ! -1) { MakeFunction(addr); count count 1; } } Message(Marked %d functions.\n, count); }這段腳本的邏輯是遍歷指定地址范圍遇到PUSH指令且操作數(shù)包含LR時(shí)認(rèn)為這是一個(gè)函數(shù)入口調(diào)用MakeFunction將其標(biāo)記為函數(shù)。參數(shù)0x08000000和0x08100000需要根據(jù)你的固件實(shí)際地址范圍修改。GetMnem獲取助記符GetDisasm獲取反匯編文本MakeFunction是 IDA 的核心 API。運(yùn)行后你會(huì)在輸出窗口看到標(biāo)記的函數(shù)數(shù)量。如果數(shù)量為 0說(shuō)明你的地址范圍不對(duì)或者處理器類型選錯(cuò)了。3. IDA5.0 核心操作交叉引用、重命名與圖形視圖3.1 交叉引用逆向分析的“導(dǎo)航儀”交叉引用是 IDA 最核心的功能之一。當(dāng)你看到一個(gè)函數(shù)被調(diào)用或者一個(gè)字符串被引用你需要知道“誰(shuí)用了它”和“它用了誰(shuí)”。在 IDA5.0 里快捷鍵X是查看交叉引用的入口。把光標(biāo)放在一個(gè)函數(shù)名或變量上按X會(huì)彈出一個(gè)窗口列出所有引用該地址的位置。舉個(gè)例子你在反匯編窗口里看到一個(gè)字符串a(chǎn)dmin想知道哪里比較了這個(gè)字符串。光標(biāo)放在admin上按X如果只有一個(gè)引用IDA 會(huì)直接跳轉(zhuǎn)過(guò)去如果有多個(gè)會(huì)列出列表。這個(gè)操作在分析登錄邏輯時(shí)特別有用。常見(jiàn)做法是先找字符串再找引用然后順著引用往上追直到找到判斷分支。// 假設(shè)反匯編中看到如下代碼片段 // 0x08001234: CMP R0, #0 // 0x08001236: BEQ loc_08001250 // 0x08001238: LDR R1, aAdmin ; admin // 0x0800123C: MOV R0, R4 // 0x08001240: BL strcmp // 0x08001244: CMP R0, #0 // 0x08001246: BNE loc_08001260這段匯編的邏輯是先比較 R0 是否為 0然后加載admin字符串地址調(diào)用strcmp再判斷返回值。如果你在strcmp處按X可以看到所有調(diào)用strcmp的地方從而快速定位所有字符串比較邏輯。參數(shù)方面X是全局交叉引用CtrlX在某些版本里是查看特定類型的引用。IDA5.0 的交叉引用窗口里p表示程序調(diào)用r表示讀取w表示寫(xiě)入。3.2 重命名與注釋讓反匯編代碼可讀IDA 自動(dòng)生成的名稱通常是sub_08001234、loc_08001250、dword_08002000這種讀起來(lái)非常痛苦。你需要手動(dòng)重命名。快捷鍵N是重命名;是添加注釋:是添加可重復(fù)注釋。重命名函數(shù)時(shí)盡量用有意義的英文比如check_password、decrypt_config。注釋可以寫(xiě)中文但要注意 IDA5.0 對(duì)中文的支持在某些系統(tǒng)上會(huì)亂碼建議用英文或拼音。# IDC 腳本批量重命名函數(shù) #include idc.idc static main() { auto addr; // 假設(shè)我們已經(jīng)知道幾個(gè)關(guān)鍵函數(shù)的地址 RenameAddr(0x08001234, check_password); RenameAddr(0x08001250, decrypt_config); RenameAddr(0x08001260, send_response); // 給特定地址添加注釋 MakeComm(0x08001234, Entry of password check); MakeComm(0x08001250, AES decrypt routine); Message(Renamed and commented.\n); }RenameAddr的第一個(gè)參數(shù)是地址第二個(gè)參數(shù)是新名稱。MakeComm用于添加注釋。運(yùn)行后反匯編窗口里的名稱會(huì)立即更新。這個(gè)腳本適合在分析初期快速標(biāo)記已知函數(shù)。如果你不知道地址可以先手動(dòng)重命名幾個(gè)然后用GetName和GetFunctionName在腳本里動(dòng)態(tài)獲取。3.3 圖形視圖與文本視圖的切換IDA5.0 默認(rèn)是文本視圖但圖形視圖能更直觀地展示控制流。按空格鍵可以在圖形視圖和文本視圖之間切換。圖形視圖里每個(gè)基本塊是一個(gè)方框箭頭表示跳轉(zhuǎn)。對(duì)于復(fù)雜的條件分支圖形視圖能幫你快速理清邏輯。但要注意IDA5.0 的圖形視圖在分析大型固件時(shí)可能會(huì)很卡因?yàn)殇秩舅谢緣K需要大量?jī)?nèi)存。常見(jiàn)做法是先用文本視圖快速瀏覽遇到復(fù)雜函數(shù)再切圖形視圖。注意IDA5.0 的圖形視圖不支持鼠標(biāo)滾輪縮放只能用鍵盤(pán)的和-鍵。如果你覺(jué)得卡可以在 Options - General 里關(guān)閉 “Auto resize graph”。4. 用 IDC 腳本擴(kuò)展 IDA5.0批量分析與自動(dòng)化4.1 IDC 腳本基礎(chǔ)變量、循環(huán)與常用 APIIDC 是 IDA 自帶的腳本語(yǔ)言語(yǔ)法類似 C但不需要編譯直接解釋執(zhí)行。在 IDA5.0 里你可以通過(guò) File - Script file 運(yùn)行 .idc 腳本也可以在命令行窗口里逐行輸入。IDC 的變量不需要聲明類型直接賦值即可。常用 API 包括ScreenEA()獲取當(dāng)前光標(biāo)地址GetDisasm(addr)獲取反匯編文本GetMnem(addr)獲取助記符NextHead(addr)獲取下一個(gè)指令地址PrevHead(addr)獲取上一個(gè)指令地址。# 遍歷當(dāng)前函數(shù)的所有指令并打印 #include idc.idc static main() { auto start, end, addr; start GetFunctionAttr(ScreenEA(), FUNCATTR_START); end GetFunctionAttr(ScreenEA(), FUNCATTR_END); if (start -1) { Message(No function at cursor.\n); return; } Message(Function start: 0x%X, end: 0x%X\n, start, end); for (addr start; addr end; addr NextHead(addr)) { Message(0x%X: %s\n, addr, GetDisasm(addr)); } }這段腳本先獲取光標(biāo)所在函數(shù)的起始和結(jié)束地址然后遍歷每條指令并打印地址和反匯編文本。FUNCATTR_START和FUNCATTR_END是函數(shù)屬性常量。NextHead會(huì)跳過(guò)數(shù)據(jù)字節(jié)只返回指令地址。這個(gè)腳本可以用來(lái)導(dǎo)出函數(shù)的完整反匯編清單方便離線分析。4.2 批量提取字符串與交叉引用在分析固件時(shí)經(jīng)常需要提取所有字符串并找到它們的引用。IDA5.0 的 Strings 窗口ShiftF12可以列出所有字符串但手動(dòng)一個(gè)個(gè)看效率太低。用 IDC 腳本可以批量導(dǎo)出字符串及其引用地址。# 導(dǎo)出所有字符串及其交叉引用 #include idc.idc static main() { auto addr, str, xref; auto fh; fh fopen(strings_xref.txt, w); if (fh 0) { Message(Cannot open output file.\n); return; } // 遍歷所有字符串 for (addr 0; addr 0xFFFFFFFF; addr NextStr(addr)) { str GetString(addr); if (str ) continue; fprintf(fh, String at 0x%X: %s\n, addr, str); // 查找交叉引用 xref RfirstB(addr); while (xref ! BADADDR) { fprintf(fh, Referenced from 0x%X\n, xref); xref RnextB(addr, xref); } } fclose(fh); Message(Export done.\n); }NextStr遍歷所有字符串GetString獲取字符串內(nèi)容RfirstB和RnextB用于遍歷交叉引用。輸出文件strings_xref.txt會(huì)包含每個(gè)字符串的地址、內(nèi)容和引用位置。這個(gè)腳本在分析網(wǎng)絡(luò)協(xié)議或配置文件時(shí)特別有用。參數(shù)方面0xFFFFFFFF是地址上限對(duì)于 32 位固件夠用。如果你的固件地址空間更大需要調(diào)整這個(gè)值。4.3 用 IDC 修補(bǔ)二進(jìn)制與導(dǎo)出補(bǔ)丁IDA5.0 不僅能看還能改。你可以用 IDC 腳本修改指令或數(shù)據(jù)然后導(dǎo)出補(bǔ)丁文件。這在做固件破解或 CTF 題目時(shí)很常見(jiàn)。比如你發(fā)現(xiàn)一個(gè)條件跳轉(zhuǎn)BEQ導(dǎo)致驗(yàn)證失敗想把它改成BNE或者直接NOP。# 將指定地址的指令改為 NOP #include idc.idc static main() { auto addr; addr 0x08001236; // 要修改的地址 // ARM 模式下 NOP 是 0x00 0x00 0xA0 0xE1小端序 PatchByte(addr, 0x00); PatchByte(addr 1, 0x00); PatchByte(addr 2, 0xA0); PatchByte(addr 3, 0xE1); Message(Patched at 0x%X\n, addr); }PatchByte用于修改單個(gè)字節(jié)。ARM 的 NOP 指令機(jī)器碼是E1A00000小端序存儲(chǔ)為00 00 A0 E1。修改后你需要通過(guò) File - Produce file - Create DIF file 導(dǎo)出差異文件或者直接用十六進(jìn)制編輯器修改原文件。注意IDA5.0 的補(bǔ)丁功能不會(huì)自動(dòng)保存到原文件你必須在 IDA 里應(yīng)用補(bǔ)丁后用 “Apply patches to input file” 功能寫(xiě)回。5. 避坑與排查IDA5.0 常見(jiàn)的五個(gè)翻車(chē)現(xiàn)場(chǎng)5.1 加載裸二進(jìn)制后全是數(shù)據(jù)沒(méi)有代碼現(xiàn)象打開(kāi)固件后反匯編窗口顯示的都是db偽指令沒(méi)有指令。原因加載時(shí)處理器類型選錯(cuò)或者加載地址不對(duì)。解決重新加載文件在加載對(duì)話框里正確選擇處理器類型比如 ARM、MIPS、PowerPC并確認(rèn)加載地址與固件實(shí)際映射地址一致。如果不確定可以先選 “Binary file” 然后手動(dòng)指定。5.2 交叉引用窗口為空現(xiàn)象按X后沒(méi)有任何引用顯示。原因IDA 還沒(méi)有完成自動(dòng)分析或者該地址確實(shí)沒(méi)有被引用。解決先按Esc回到反匯編窗口然后等待 IDA 右下角的 “AU:” 進(jìn)度條走完。如果進(jìn)度條卡住可以手動(dòng)觸發(fā)分析Options - General - Analysis - Reanalyze program。另外如果地址是數(shù)據(jù)而不是代碼交叉引用可能只顯示讀取引用需要檢查是否在正確的段里。5.3 IDC 腳本運(yùn)行報(bào)錯(cuò) “Undefined variable”現(xiàn)象腳本里用了某個(gè)變量但 IDA 提示未定義。原因IDC 是弱類型語(yǔ)言但某些 API 返回值需要先判斷再使用。解決在腳本開(kāi)頭用auto聲明所有變量或者直接賦值。比如auto addr; addr ScreenEA();。另外IDC 不支持for (auto i 0; ...)這種寫(xiě)法必須把a(bǔ)uto放在循環(huán)外面。5.4 中文注釋亂碼現(xiàn)象在注釋里輸入中文保存后重新打開(kāi)變成亂碼。原因IDA5.0 默認(rèn)使用系統(tǒng)編碼而中文 Windows 是 GBK但 IDA 內(nèi)部可能按 Latin-1 處理。解決盡量用英文注釋。如果必須用中文可以在 Options - General - Strings 里把編碼改成 “GBK” 或 “UTF-8”但不同版本行為不一致。血淚經(jīng)驗(yàn)是中文注釋只在當(dāng)前會(huì)話有效重啟后大概率亂碼重要信息用拼音或英文。5.5 圖形視圖卡死或顯示不全現(xiàn)象切換到圖形視圖后IDA 無(wú)響應(yīng)或者只顯示部分基本塊。原因函數(shù)太大基本塊太多IDA5.0 的圖形引擎處理不過(guò)來(lái)。解決按空格鍵切回文本視圖。如果必須看圖形可以先用 IDC 腳本把大函數(shù)拆分成多個(gè)小函數(shù)或者用CtrlShiftG只顯示當(dāng)前基本塊。另外關(guān)閉 “Auto resize graph” 選項(xiàng)能減少卡頓。6. 進(jìn)階技巧用 IDA5.0 做固件差分與版本對(duì)比當(dāng)你手里有兩個(gè)版本的固件想知道改了什么IDA5.0 可以配合 IDC 腳本做差分。核心思路是分別加載兩個(gè)固件提取函數(shù)地址、名稱和反匯編文本然后對(duì)比差異。常見(jiàn)做法是寫(xiě)一個(gè)腳本把每個(gè)函數(shù)的起始地址、大小和指令哈希輸出到文件然后用 diff 工具對(duì)比。# 導(dǎo)出函數(shù)指紋 #include idc.idc static main() { auto addr, func, fh; fh fopen(func_fingerprint.txt, w); for (addr 0; addr 0xFFFFFFFF; addr NextFunc(addr)) { func GetFunctionName(addr); if (func ) continue; fprintf(fh, 0x%X %s %d\n, addr, func, GetFunctionAttr(addr, FUNCATTR_END) - addr); } fclose(fh); Message(Fingerprint exported.\n); }NextFunc遍歷所有函數(shù)GetFunctionName獲取函數(shù)名FUNCATTR_END獲取結(jié)束地址。輸出文件包含地址、名稱和大小。對(duì)兩個(gè)固件分別運(yùn)行這個(gè)腳本然后用diff命令對(duì)比。如果函數(shù)名相同但大小變了說(shuō)明函數(shù)邏輯有修改如果地址變了但大小相同可能是代碼移位。這個(gè)技巧在分析固件更新時(shí)非常實(shí)用能快速定位改動(dòng)點(diǎn)。提示IDA5.0 的NextFunc在某些情況下會(huì)跳過(guò)未識(shí)別的函數(shù)。如果發(fā)現(xiàn)函數(shù)數(shù)量不對(duì)先用MakeFunction手動(dòng)標(biāo)記可疑區(qū)域。另一個(gè)進(jìn)階用法是結(jié)合 IDA5.0 的 FLIRT 簽名。FLIRT 是 Fast Library Identification and Recognition Technology能自動(dòng)識(shí)別標(biāo)準(zhǔn)庫(kù)函數(shù)。在 IDA5.0 里你可以通過(guò) File - Load file - FLIRT signature file 加載 .sig 文件。常見(jiàn)做法是加載vc32rtf.sig識(shí)別 Visual C 運(yùn)行時(shí)函數(shù)或者armlib.sig識(shí)別 ARM 標(biāo)準(zhǔn)庫(kù)。加載后IDA 會(huì)把sub_08001234自動(dòng)重命名為memcpy之類的標(biāo)準(zhǔn)函數(shù)名大大減少分析工作量。最后說(shuō)一個(gè)我自己的習(xí)慣每次用 IDA5.0 分析新固件先跑一遍自動(dòng)分析然后立刻用 IDC 腳本導(dǎo)出所有字符串和函數(shù)列表存成文本。這樣即使 IDA 崩潰或者數(shù)據(jù)庫(kù)損壞你還有一份離線記錄。IDA5.0 雖然老但它的穩(wěn)定性和腳本靈活性在特定場(chǎng)景下依然沒(méi)有替代品。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取