2015/03/03

x86 軟體逆向工程,以 x64_dbg 為例

什麼是逆向工程?不解釋。

需要什麼工具?反組譯工具,在 PC-based 上跑的程式,需要用 x86 反組譯程式,目前我在 Windows 上用過的三套 IDA pro freeollydbgx64_dbg,都是免費的,而其中 x64_dbg 還是 Open Source,也是我取得的軟體之中唯一支援 x86 64 bits 反組譯(IDA pro 6 也有支援,但是是收費版本),ollydbg 的 64 bits 支援還在穩定發展中。而 IDA pro free 可以解析資源檔,其他兩套則不行。

還需要什麼呢?初步的逆向工程不需要高深的 x86 組合語言能力,只要知道 call、ret 還有 j 系列的指令集,如 je、jmp...

本篇將以 x64_dbg 的 32 bit 版本 x32_dbg 來實做一次逆向工程,以某 shareware 為例而不是自己寫一個來操作。

x64_dbg 的操作就不贅述,本篇也不介紹比較進階的搜尋作為,只從程式進入點開始以苦力的方式進行逆向工程。

1.

首先安裝 shareware,然後執行,並觀察其執行流程。後續稱此程式為 A

執行時,出現對話窗,告知此軟體不是免費軟體,當按下 I agree 之後開始讀秒,3 秒讀完之後就進入程式了。

這個流程非常單純,這次逆向工程的目標也很單純,跳過這個宣告,直接進入程式。

2.

首先執行 x32_dbg,然後載入 A 程式,反組譯過的程式碼映入眼簾,多美麗啊!

3.

接著就是按住 [Shift] + [F8] 不斷地執行 step-over,[F8] step-over 跟 [F7] step-into 的差別在 [F8] 遇到 call 指令集時(function call),會完整執行該 call 回到下一個指令,而 [F7] 則會進入該 call 位址繼續執行。按住 [Shift] 則可以不理會 call 裡面的例外,要不然遇到例外是會中斷在例外點,那就壞了 step-over 的優勢了。

在這個 routine 的 step-over 進行的過程中,要特別留意 j 系列的指令沒有往回跳的情形,遇到這種狀況通常是迴圈,應該要設法直接跑到迴圈結束的地方,而不是 step-over 慢慢把迴圈跑完。

3.1

x64_dbg 的介面會標示  j 系列指令集的跳躍位置,執行到該指令時,該線條是紅色的就表示要跳躍,如果是深灰色的就表示不跳躍。

上圖是一個迴圈結構,從 0046C9AF 開始到 0046CA3E 跳回。
回圈內有以下跳出點
0046C9CA | JNB a.46CAAB                            |
0046CA1B | JE a.46CA43                             |
第一個跳出點比較遠,所以取出回圈起點 0046C9AF(D1)到第一個跳點目標 0046CAAB(D2)之間的所有程式出來看,要留意各個 J 的目標是不是介於 D1 跟 D2 之間
0046C9AF | MOV ECX,DWORD PTR SS:[EBP-A80]          |
0046C9B5 | ADD ECX,1                               |
0046C9B8 | MOV DWORD PTR SS:[EBP-A80],ECX          |
0046C9BE | MOV EDX,DWORD PTR SS:[EBP-A80]          |
0046C9C4 | CMP EDX,DWORD PTR SS:[EBP-A88]          |
0046C9CA | JNB a.46CAAB                            |
0046C9D0 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9D6 | CALL a.46F570                           |
0046C9DB | MOV DWORD PTR SS:[EBP-4],0              |
0046C9E2 | MOV EAX,DWORD PTR SS:[EBP+C]            |
0046C9E5 | PUSH EAX                                |
0046C9E6 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9EC | PUSH ECX                                |
0046C9ED | CALL a.46BE70                           |
0046C9F2 | ADD ESP,8                               |
0046C9F5 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9FB | CALL a.470870                           |
0046CA00 | ADD EAX,270                             |
0046CA05 | MOV DWORD PTR SS:[EBP-A8C],EAX          |
0046CA0B | MOV ECX,DWORD PTR SS:[EBP-A8C]          |
0046CA11 | CALL a.394830                           |
0046CA16 | MOVZX EDX,AL                            |
0046CA19 | TEST EDX,EDX                            |
0046CA1B | JE a.46CA43                             |
0046CA1D | MOV EAX,DWORD PTR SS:[EBP-A84]          |
0046CA23 | ADD EAX,1                               |
0046CA26 | MOV DWORD PTR SS:[EBP-A84],EAX          |
0046CA2C | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA33 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CA39 | CALL a.3EF400                           |
0046CA3E | JMP a.46C9AF                            |
0046CA43 | PUSH 0                                  |
0046CA45 | PUSH a.9DF040                           | ;9DF040:"64.14.19.49"
0046CA4A | MOV ECX,DWORD PTR SS:[EBP-A8C]          |
0046CA50 | CALL a.395150                           |
0046CA55 | CMP EAX,DWORD PTR DS:[9D3630]           |
0046CA5B | JE a.46CA83                             |
0046CA5D | MOV ECX,DWORD PTR SS:[EBP-A7C]          |
0046CA63 | ADD ECX,1                               |
0046CA66 | MOV DWORD PTR SS:[EBP-A7C],ECX          |
0046CA6C | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA73 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CA79 | CALL a.3EF400                           |
0046CA7E | JMP a.46C9AF                            |
0046CA83 | PUSH 1                                  |
0046CA85 | LEA EDX,DWORD PTR SS:[EBP-A78]          |
0046CA8B | PUSH EDX                                |
0046CA8C | MOV ECX,DWORD PTR SS:[EBP+8]            |
0046CA8F | CALL a.470420                           |
0046CA94 | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA9B | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CAA1 | CALL a.3EF400                           |
0046CAA6 | JMP a.46C9AF                            |
0046CAAB | PUSH a.9DF2FC                           | ;9DF2FC:" records."
因為所有 J 系列指令的目標都在範圍內,所以這個迴圈可以封閉,我們可以放心的讓他跑完,直接到 0046CAAB 等他。方法是點擊 0046CAAB 把焦點轉到該位址,然後按下 [F4] run until selection,如此就不必耗費太多時間在這個迴圈內。

後面又遇到一個迴圈,比上面那個迴圈稍微複雜一點點
003EE21A | LEA ECX,DWORD PTR SS:[EBP-4]            |
003EE23F | JE a.3EE2A8                             |
003EE265 | JE a.3EE2A3                             |
003EE289 | JE a.3EE2A3                             |
003EE2A1 | JMP a.3EE2AA                            |
003EE2A3 | JMP a.3EE21A                            |
003EE2A8 | XOR AL,AL                               |
003EE2AA | MOV ESP,EBP                             |
迴圈中有兩個跳出點,一個到 3EE2A8,另一個到 3EE2AA,由此判斷 3EE2A8 不是必經途徑,而 3EE2AA 才是必經途徑,所以我們在 3EE2AA 等。同樣祭出 [F4] 伺候。

4.

注意注意!發現目標。
執行到 0081153E 這個 call 時,終於出現了對話窗,趕快先按下 [F2] 設定中斷點。然後觀察,跑完流程,連主程式都跑完,才回到 dbg 控制。所以下次要進入這個中斷點 call 的目標裡面去看。

這裡有一個方便記憶的工具,在該 call 加註釋會很好用,方法是點選該指令,按下 [;] 分號,就可以給他加註解了,因為是第一個,我給他加註 1st,後面會用到。

按下左上角的按鈕,重新載入。

這次直接按下 [F9] 執行,程式會直接跑到中斷點,然後千萬別按 [F8] ,而是按  [F7],因為我們要跑進去看了。

4.1

又發現一個目標。
同樣按下 [F2] 加入中斷點並加註,然後切換到中斷點頁籤,把上一個中斷點設定成 disable,這樣下次重啟後按下 [F9] 就會跳過前一個中斷點,直接跑到最後一個中斷點,不把他刪除是因為說不定還會用到。

繼續 routine 執行,要找到只關閉對話框,而還沒把主程式跑完的地方。

4.2

皇天不負苦力人,第三次就遇到出現對話窗,而還沒進入主程式,同樣設中斷點並加註。接下來要好好利用這個中斷點。

5.

重啟後,在這個中斷點我們故意給他跳過不執行,然後觀察程式的行為。到 command 執行 eip=中斷點的下一個位址,然後按下 [F9]。

真好運,直接進入主程式了。

6.

既然直接跳過對話窗還是進入主程式,那就直接把對話窗跳開吧。
在 4.2 的中斷點按下滑鼠右鍵,選擇 binary->Fill with NOP,就可以把 call 指令變成 NOP 了

然後點顯上方快速按鈕中間稍微偏右的出現 pach 對話框
點選右下角的 Patch File,對執行檔進行 patch,程式會問你要存成什麼檔名,就存成 B.exe 吧,執行 B.exe,直接就進入主畫面了。

7.

預期評估日期結束後,會有其他流程檢查日期,屆時可以再苦力一次。

8.

x64_dbg 的資料夾裡面有一個子資料夾叫做 [db],用來儲存 x64_dbg 的操作,包括中斷點跟註解,下次載入該執行檔時,就能直接使用,非常方便。裡面的檔案不要刪掉,評估日期結束後,會需要再用的。實際上 x64_dbg  有把定字串顯示出來,對程式流程控制有概念的話,在 4.2 那附近就能把評估日期也一併處理掉了,那會需要更多的經驗,這裡只做入門苦力,就不提了,而且幾乎每支程式都會不一樣,多提也沒什麼意思。

沒有留言: