Make your own free website on Tripod.com

中文化提高課─解除軟體自檢驗

作者: 漢化新世紀成員飛鷹

在中文化一個軟體之前,我想首先擺在我們中文化人之前的就有兩大難題: 一是軟體被加了殼,二是軟體自身有自檢驗;如果我們無法很好的處理這兩個難題的話, 將不能進行軟體的中文化。就此,我以我多年的中文化經驗寫了兩篇《中文化提高課》文章,一篇是關於軟體脫殼的,一篇 (此篇) 是關於解除軟體自檢驗的,衷心希望各位中文化愛好者能在閱讀完這兩篇文章之後,中文化技術會有進一步的提高,我寫這兩篇文章的目的也就達到了。

什麼是自檢驗 ? 自檢驗是軟體作者給軟體加保護的一種方式,它是透過檢查程式的完整性來防止其它人修改軟體。這種完整性檢查就是針對程式的某一部分或全部計算出一個值來,然後和預期的值相比較。如果不同,則說明程式被人修改過了,這有可能是感染了病毒,也可能是被破解了;可讓程式在這種情況下拒絕執行。

軟體的自檢驗要如何才能解除呢 ? 我們可以透過一些動態調試器 (如Softice、Trw2000、OllyDbg等) ,跟蹤軟體的執行過程來判斷出軟體的自檢驗處 (如果您的編譯高手的話,您還可以分析出自檢驗的算法) ,之後修改軟體讓其跳過這段自檢驗代碼的檢驗就可以達到解除軟體自檢驗的目的了。俗話說: 欲成其事,先利其器;那麼我們要先選擇一種動態調試軟體才能進行下面的工作,用Softice、Trw2000來調試是完全可行的,加上這兩個調試器的功能相當強大,但我想大多數中文化人一看到這種調試器就頭痛,那還會有心情再把此篇文章看一去;為了讓大家能能掌握一些跟蹤調試軟體的方法,也為了讓我這篇文章不白寫,我就選用最近調試器中的新成員 OllyDbg 來進行下面的工作您 !

OllyDbg 是一個32位編譯級的直觀的分析調試器,它在源代碼不可得或者您用編譯器遇到問題的時候特別有用,現在最高版本是1.06,它的界面如圖一所示,界面上共分為四個視窗,左上角是編譯區,右上角是暫存器區,左下角是記憶體區,右下角是入棧出棧區。OllyDbg 不像Softice、Trw2000那樣在執行時需要佔用整個系統的全部資源,它在執行時可以一邊調試、一邊進行其它工作 (比如聽MP3);並且,它的調試環境是友好的圖形界面,需要什麼命令您都可以在選單中選擇,不必再記任何命令,如何您想調試速度更快的話,您可能需要記一些 OllyDbg 常用的快速鍵。選擇好順手的兵器 (OllyDbg 1.06) 後,下面我們就開始學習解除軟體自檢驗的方法。

(圖一)

1、自檢驗保護軟體 CpuIdle 5.9: 這個軟體是應我們 漢化新世紀成員-五哥 的要求,進行的調試修改。首先,我們知道 CpuIdle 5.9 被UPX1.20加了殼,脫殼後 (脫殼方法可以參看我寫的另一篇文章《中文化提高課─UPXPR 的脫殼方法》) 再次啟動軟體,您就會發現軟體執行不起來,沒有任何的錯誤提示就退出。下面我們啟動 OllyDbg 來進行跟蹤分析,操作步驟: 選擇「File」主選單中的「Open」項目 (快速鍵為F3) 載入軟體-->稍等片刻後出現如圖一所示的界面,之後按 F8 鍵 (也可以選擇「Debug」主選單中的「Step over」項目) 進行單步調試-->一直按 F8 鍵來到 0048BB4C 處後 (指令為JNZ SHORT CPUIDLE5.0048BB7D) ,此時再按 F8 鍵將跳轉到 0048BB7D 處,其中從 0048BAE9至0048BB78 之間的指令將不會被執行到,從 0048BB7D 處按 F8 鍵往後走不到幾條指令 OllyDbg 將會中止跟蹤過程。所以,我們就可以斷定 0048BB4C 處正是軟體自檢驗的檢測處;如果此處跳轉的話,程式將退出執行;如果此處不跳轉的話,程式將會啟動繼續執行-->知道了軟體自檢驗的檢測處是 0048BB4C 後,選擇「Debug」主選單中的「Restart」項目 (快速鍵為Ctrl+F2) 重新載入該軟體,當跟蹤到 0048BB4C 處時,在左上角的編譯區中單擊滑鼠右鍵選擇「Copy to executable file」-->在進入的新視窗中單擊滑鼠右鍵選擇「Assemble」項目把指令 JNZ SHORT CPUIDLE5.0048BB7D 改為 NOP (注意要選中該對話框中的「Fill with NOP's」選項) (註: NOP指令為空指令,即不執行任何操作) ,單擊「Assemble」按鈕修改成功,之後關閉新開啟這個視窗時 OllyDbg 會詢問您「檔案已經被修改,是否要保存 ? 」,單擊「是」按鈕後您修改的地方就被永久的保存到 EXE 檔案中去了 (免去再用 16 進制修改一次的麻煩) 。關閉 OllyDbg 後,再次啟動新保存的 CpuIdle 5.9,您會發現軟體已經可以正常執行了。下面是調試該軟體時在 OllyDbg 左上角的編譯區中顯示出來的內容。其中,『//』是我自己加入的註釋,方便大家理解;『;』是 OllyDbg 自動分析出來的字串。

0048B9FC >/$ 55            PUSH EBP                         //載入軟體後首先停在這裡
0048B9FD |. 8BEC           MOV EBP,ESP
0048B9FF |. 83C4 E0        ADD ESP,-20
0048BA02 |. 33C0           XOR EAX,EAX
0048BA04 |. 8945 E0        MOV DWORD PTR SS:[EBP-20],EAX
0048BA07 |. 8945 E4        MOV DWORD PTR SS:[EBP-1C],EAX
0048BA0A |. 8945 EC        MOV DWORD PTR SS:[EBP-14],EAX
0048BA0D |. 8945 E8        MOV DWORD PTR SS:[EBP-18],EAX
0048BA10 |. B8 A4B74800    MOV EAX,CPUIDLE5.0048B7A4
0048BA15 |. E8 2EB2F7FF    CALL CPUIDLE5.00406C48
0048BA1A |. 33C0           XOR EAX,EAX
0048BA1C |. 55             PUSH EBP
0048BA1D |. 68 A3BB4800    PUSH CPUIDLE5.0048BBA3
0048BA22 |. 64:FF30        PUSH DWORD PTR FS:[EAX]
0048BA25 |. 64:8920        MOV DWORD PTR FS:[EAX],ESP
0048BA28 |. 8D55 E8        LEA EDX,DWORD PTR SS:[EBP-18]
0048BA2B |. 33C0           XOR EAX,EAX
0048BA2D |. E8 6A70F7FF    CALL CPUIDLE5.00402A9C
0048BA32 |. 8B45 E8        MOV EAX,DWORD PTR SS:[EBP-18]
0048BA35 |. 8D55 EC        LEA EDX,DWORD PTR SS:[EBP-14]
0048BA38 |. E8 2FD8F7FF    CALL CPUIDLE5.0040926C
0048BA3D |. 8B55 EC        MOV EDX,DWORD PTR SS:[EBP-14]
0048BA40 |. A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA45 |. E8 3A8EF7FF    CALL CPUIDLE5.00404884
0048BA4A |. A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA4F |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BA51 |. E8 9290F7FF    CALL CPUIDLE5.00404AE8
0048BA56 |. 8B15 C0D54800  MOV EDX,DWORD PTR DS:[48D5C0]   ; CPUIDLE5.0048CFAC
0048BA5C |. 8B12           MOV EDX,DWORD PTR DS:[EDX]
0048BA5E |. 807C02 FF 5C   CMP BYTE PTR DS:[EDX+EAX-1],5C
0048BA63 |. 74 14          JE SHORT CPUIDLE5.0048BA79
0048BA65 |. A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA6A |. BA B8BB4800    MOV EDX,CPUIDLE5.0048BBB8
0048BA6F |. E8 7C90F7FF    CALL CPUIDLE5.00404AF0
0048BA74 |. A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA79 |> A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA7E |. BA C4BB4800    MOV EDX,CPUIDLE5.0048BBC4       ; ASCII "cpuidle.ini"
0048BA83 |. E8 6890F7FF    CALL CPUIDLE5.00404AF0
0048BA88 |. A1 C0D54800    MOV EAX,DWORD PTR DS:[48D5C0]
0048BA8D |. A1 08DA4800    MOV EAX,DWORD PTR DS:[48DA08]
0048BA92 |. 8338 02        CMP DWORD PTR DS:[EAX],2
0048BA95 |. 75 2A          JNZ SHORT CPUIDLE5.0048BAC1     //判斷您是不是在 WinNT 下執行該軟體,如果此處不跳轉則退出程式執行。
0048BA97 |. 6A 00          PUSH 0
0048BA99 |. 8D4D E4        LEA ECX,DWORD PTR SS:[EBP-1C]
0048BA9C |. BA C4130000    MOV EDX,13C4
0048BAA1 |. B8 D8BB4800    MOV EAX,CPUIDLE5.0048BBD8      ; ASCII "Sorry- This version of CpuIdle does not run on Windows NT"
0048BAA6 |. E8 C969FEFF    CALL CPUIDLE5.00472474
0048BAAB |. 8B45 E4        MOV EAX,DWORD PTR SS:[EBP-1C]  ; |
0048BAAE |. 66:8B0D 14BC48>MOV CX,WORD PTR DS:[48BC14]    ; |
0048BAB5 |. B2 01          MOV DL,1                       ; |
0048BAB7 |. E8 BCA7FAFF    CALL CPUIDLE5.00436278         ; \CPUIDLE5.00436278
0048BABC |. E9 C7000000    JMP CPUIDLE5.0048BB88
0048BAC1 |> 68 18BC4800    PUSH CPUIDLE5.0048BC18         ; /Arg3 = 0048BC18 ASCII "CpuIdle"
0048BAC6 |. 6A 00          PUSH 0                         ; |Arg2 = 00000000
0048BAC8 |. 6A 00          PUSH 0                         ; |Arg1 = 00000000
0048BACA |. E8 29B3F7FF    CALL CPUIDLE5.00406DF8         ; \CPUIDLE5.00406DF8
0048BACF |. A3 0CF04800    MOV DWORD PTR DS:[48F00C],EAX
0048BAD4 |. E8 DFB3F7FF    CALL <JMP.&KERNEL32.GetLastError> ; [GetLastError
0048BAD9 |. 3D B7000000    CMP EAX,0B7
0048BADE |. 74 09          JE SHORT CPUIDLE5.0048BAE9
0048BAE0 |. 833D 0CF04800 >CMP DWORD PTR DS:[48F00C],0
0048BAE7 |. 75 27          JNZ SHORT CPUIDLE5.0048BB10     //判斷 CpuIdle 是否已經執行,如果此處不跳轉則退出程式執行。
0048BAE9 |> 6A 00          PUSH 0
0048BAEB |. 8D4D E0        LEA ECX,DWORD PTR SS:[EBP-20]
0048BAEE |. BA BE130000    MOV EDX,13BE
0048BAF3 |. B8 28BC4800    MOV EAX,CPUIDLE5.0048BC28      ; ASCII "CpuIdle is already running."
0048BAF8 |. E8 7769FEFF    CALL CPUIDLE5.00472474
0048BAFD |. 8B45 E0        MOV EAX,DWORD PTR SS:[EBP-20]  ; |
0048BB00 |. 66:8B0D 14BC48>MOV CX,WORD PTR DS:[48BC14]    ; |
0048BB07 |. B2 01          MOV DL,1                       ; |
0048BB09 |. E8 6AA7FAFF    CALL CPUIDLE5.00436278         ; \CPUIDLE5.00436278
0048BB0E |. EB 78          JMP SHORT CPUIDLE5.0048BB88
0048BB10 |> A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB15 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB17 |. E8 8013FDFF    CALL CPUIDLE5.0045CE9C
0048BB1C |. A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB21 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB23 |. BA 4CBC4800    MOV EDX,CPUIDLE5.0048BC4C      ; ASCII "CpuIdle"
0048BB28 |. E8 7B0FFDFF    CALL CPUIDLE5.0045CAA8
0048BB2D |. A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB32 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB34 |. C740 74 393000>MOV DWORD PTR DS:[EAX+74],3039
0048BB3B |. E8 0462FDFF    CALL CPUIDLE5.00461D44
0048BB40 |. A1 40D64800    MOV EAX,DWORD PTR DS:[48D640]
0048BB45 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB47 |. 35 3CB7D21F    XOR EAX,1FD2B73C
0048BB4C |. 75 2F          JNZ SHORT CPUIDLE5.0048BB7D     //軟體自檢驗的關鍵判斷處,如果此處跳轉則退出程式執行。
0048BAE9 |> 6A 00          PUSH 0
0048BB4E |. A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB53 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB55 |. C640 5B 00     MOV BYTE PTR DS:[EAX+5B],0
0048BB59 |. 8B0D FCD94800  MOV ECX,DWORD PTR DS:[48D9FC]  ; CPUIDLE5.0048F004
0048BB5F |. A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB64 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB66 |. 8B15 F0724800  MOV EDX,DWORD PTR DS:[4872F0]  ; CPUIDLE5.0048733C
0048BB6C |. E8 4313FDFF    CALL CPUIDLE5.0045CEB4
0048BB71 |. A1 78D84800    MOV EAX,DWORD PTR DS:[48D878]
0048BB76 |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
0048BB78 |. E8 B713FDFF    CALL CPUIDLE5.0045CF34
0048BB7D |> A1 0CF04800    MOV EAX,DWORD PTR DS:[48F00C]
0048BB82 |. 50             PUSH EAX                          ; /hMutex => NULL
0048BB83 |. E8 50B4F7FF    CALL <JMP.&KERNEL32.ReleaseMutex> ; \ReleaseMutex
0048BB88 |> 33C0           XOR EAX,EAX
0048BB8A |. 5A             POP EDX
0048BB8B |. 59             POP ECX
0048BB8C |. 59             POP ECX
0048BB8D |. 64:8910        MOV DWORD PTR FS:[EAX],EDX
0048BB90 |. 68 AABB4800    PUSH CPUIDLE5.0048BBAA
0048BB95 |> 8D45 E0        LEA EAX,DWORD PTR SS:[EBP-20]
0048BB98 |. BA 04000000    MOV EDX,4
0048BB9D |. E8 B28CF7FF    CALL CPUIDLE5.00404854
0048BBA2 \. C3             RETN

2、自檢驗保護軟體 MaxMem 1.01: 這個軟體是應一位朋友 txjgzz 的要求,進行的調試修改。這個軟體的自檢驗保護稍弱一點,只要您修改了該軟體中的任何一處,啟動軟體時它會提示說「ABORT: MaxMem has been corrupted, please re-install to correct the problem.. (winmain.c/564)」,這句話就是最好的解除自檢驗的突破口。按照上一篇操作步驟先用 OllyDbg 載入該軟體,之後選擇「Debug」主選單中的「Run」項目 (快速鍵為F9) 讓軟體執行,當該軟體跳出錯誤提示對話框時,切換到 OllyDbg 中 (注意: 此時不要關閉出錯對話框,否則下面的尋找會失敗) 按 F12 鍵來到下面這裡:

0040158B |. F605 7C014100 >TEST BYTE PTR DS:[41017C],10
00401592 |. 75 35          JNZ SHORT MAXMEM.004015C9
00401594 |. 6A 10          PUSH 10                                   ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401596 |. 68 BAF34000    PUSH MAXMEM.0040F3BA                      ; |Title = "Internal error..."
0040159B |. 68 12104100    PUSH MAXMEM.00411012                      ; |Text = "ABORT: MaxMem has been corrupted, please re-install to correct the problem. (winmain.c/564)"
004015A0 |. 6A 00          PUSH 0                                    ; |hOwner = NULL
004015A2 |. 2E:FF15 E4E340>CALL DWORD PTR CS:[<&USER32.MessageBoxA>> ; \MessageBoxA
004015A9 |. EB 1E          JMP SHORT MAXMEM.004015C9

大家是不是發現,00401592 處有一個轉移指令,那麼是否可以把 JNZ SHORT MAXMEM.004015C9 改為 JMP SHORT MAXMEM.004015C9,經過測試發現改這裡沒有作用,雖然沒有了提示框,但軟體還是無法執行。所以,接著下面繼續講解。

選擇「View」主選單中的「Memory」項目 (快速鍵為Alt+M) ,這時會產生一個新的視窗,在這個視窗中查看標題 (Owner) 為 MAXMEM 的項目,共有七個地方,其中塊 (Section) 的內容都為空 (這裡是個特例,一般 Section 的內容是.data、.code、.rsrc) ,當我們選擇 Owner 為 MAXMEM 的倒數第四處地方時 (因為這個 Section 中存放著出錯對話框中的字串,這裡我是透過不斷的測試發現的,一般這樣的字串都會存放在 Section 為.code或.data當中,但這個軟體 Section 內容都為空,所以判斷起來就困難一些) ,單擊滑鼠右鍵選擇「Dump in CPU」;這裡應該切換到 OllyDbg 的左下角是記憶體區中,單擊滑鼠右鍵選擇「Search for」選單中的「Binary string」項目,並在 ASCII 框中輸入 ABORT: MaxMem, (其實也就是啟動 MaxMem 時出現在錯誤對話框中的內容,這裡我只取了前幾個字串) 單擊「OK」按鈕,沒有找到。好您,我就尋找 has been corrupted,可以找到一處,再往下尋找就沒有了;所以,我肯定此處就是啟動軟體時出現的錯誤提示訊息,找到的字串是「%s has been corrupted, please re-install to correct the problem.」那麼看來「ABORT: MaxMem」這幾個字串是在軟體執行時自動填補進來的,單擊滑鼠右鍵選擇「Fine references」項目又進入了一個視窗,在其中顯示出了該對話框被那幾地方調用過,這裡只有一地方調用它,地址是 0040390F,在該處雙擊滑鼠左鍵來到了 OllyDbg 左上角的編譯區中,這時的狀態如下所示:

004038F1 |. E8 9DD8FFFF   CALL MAXMEM.00401193
004038F6 |. 85C0          TEST EAX,EAX
004038F8 |. 75 22         JNZ SHORT MAXMEM.0040391C
004038FA |. BA 34020000   MOV EDX,234
004038FF |. B8 F4F94000   MOV EAX,MAXMEM.0040F9F4    ; ASCII "winmain.c"
00403904 |. E8 77DDFFFF   CALL MAXMEM.00401680
00403909 |. FF35 E4014100 PUSH DWORD PTR DS:[4101E4] ; /Arg2 = 0040F894 ASCII "MaxMem"
0040390F |. 68 F3FA4000   PUSH MAXMEM.0040FAF3       ; |Arg1 = 0040FAF3 ASCII "%s has been corrupted, please re-install to correct the problem."
00403914 |. E8 6DDAFFFF   CALL MAXMEM.00401386       ; \MAXMEM.00401386

從 0040390F 向上找,可以知道只要把 004038F8 處的指令 JNZ SHORT MAXMEM.0040391C 改為 JMP SHORT MAXMEM.0040391C (註: JMP 指令是無條件轉移指令;即不論條件是否比對,它都會永遠跳轉到目標地址) ,之後就可以跳過這個出錯對話框繼續往下執行。具體的修改方法在上面我已介紹過了,這裡不再重複講述。

3、 自檢驗保護軟體 RegEditor 1.2: 這個軟體是應論壇上一位朋友 mimizero 的要求,進行的調試修改。跟蹤調試方法我就不多說了,與上面講的基本相同,只是該軟體在解除自檢驗後就無法再使用它所具有的任何功能了,只要使用該軟體的任何一個功能就自動退出執行,這種保護方式可能以後會有很多軟體開發人員使用,所以在此我也提一下。我們可以透過中斷 PostQuitMessage 函數來達到調試的目的,操作步驟: 在 OllyDbg 中按 Ctrl+N 鍵在其中選擇 user32.PostQuitMessage 函數-->按 Enter 鍵之後選中有 Call 語句的地方,按 F2 鍵下斷點-->之後,執行該軟體中任何一個功能,立刻就會被 OllyDbg 中斷,回到 OllyDbg 的主界面左上角的編譯區中向上找是否有能跳過此處的指令,比如: JNZ、JZ等,之後把找到的地方改為 JMP 就行了。對於該軟體共可以找到五處地方調用了 user32.PostQuitMessage 函數,但只要修改其中的三處地方即可完全解決該軟體執行功能自動退出的情況,其它兩處好像都是執行軟體的正常退出,決對不能改,否則軟體將不能正常退出了。

上面講到的這三種軟體自檢驗保護方式應該是目前最常見的。當然,也不是說您學會這篇文章中的全部內容就能對付所有軟體的自檢驗了,在我們的中文化過程中會碰到一些有很強自檢驗保護的軟體,比如: CacheX、flax、CuteFTP Pro、HyperSnap-DX等,這些軟體的自檢驗真可謂是最難解除的了,像 CacheX 它就的是根據自檢驗的結果來動態還原某些執行代碼,如果您改動了軟體中的任何地方,這些動態生成的代碼將得不到正確的還原,一旦執行肯定就會出錯。此類軟體如果大家遇到就只能耐心慢慢分析調試了。

OllyDbg 的網址: http://home.t-online.de/home/Ollydbg,您可以到它的網站去下載該軟體的最新版本。

飛鷹山莊中文化天地飛鷹   寫於2002年4月26日



回教學