因為是第一次破解較大型的商業軟件,所以有必要記錄一下,另外UE的價格也實在太貴了。。。
第零步:准備
准備好破解常用軟件。我常用的工具有IDA(用於靜態分析)、Ollydbg(動態調試)、PEiD(用於查看可執行文件是否加殼)、W32Dasm(在一個網上教學視頻中看到的軟件,用於靜態搜索軟件中的字符串資源)、CheatEngine(多用於游戲作弊,這里用來動態搜索進程中的字符串)。
先對Uedit32.exe這個可執行文件進行初探,PEiD發現它沒有加殼,這也不出意料,這種軟件肯定是不屑於加殼的。
其次,需要了解一下UE的注冊/激活機制:默認安裝后軟件處於未注冊/激活狀態,此時可以試用30天,試用到期后,會彈出提示,必須注冊才可以使用;未到期時也可以點擊“幫助”->“注冊”,此時看到的是UE的在線激活對話框(如下圖所示),填入許可證ID和密碼點擊“激活”按鈕后將向UE的server發起HTTP交互,沒有正確的許可證ID和密碼時當然是不能被server check通過的。
還有另一種激活方式--脫機激活,需要在禁用網卡的情況下點擊上面的“激活”按鈕才會出現:
點擊“脫機激活”,在對話框中填入“許可證”、“密碼”、“驗證碼1”、“驗證碼2”后,點擊“激活”按鈕,會彈出失敗信息:
注意這個錯誤提示信息“您輸入的代碼無效。您需要...”,本文所陳述的破解方式就從這個字符串開始。
第一步,找到訪問出錯提示的指令和函數。
首先嘗試靜態查找。使用W32dasm,查找字符串“您輸入的代碼無效。您需要...”,沒找到。
由此猜測UE的提示信息可能使用動態加載的方式,在UE啟動時從某個文件中讀取到內存中。據此嘗試使用CheatEngine附加后查找,查找成功:
繼續使用CheatEngine找出是什么訪問了這個地址,找到這樣幾個指令:
我們只應該關注UE模塊內的指令,因此這里要關注上圖中第一條指令。
嘗試對這條指令下斷點,發現程序被不停地斷下,可以猜測UE可能存在一個線程在不停地執行該處指令,另外觀察到每次斷下后堆棧都不相同,由此可見該處的指令可能位於一個底層函數內部,因此需要使用條件斷點,當寄存器EDI的值等於05A27B80時斷下。這時發現不會被反復斷下了,並且再次嘗試激活時斷到了這條指令。
然后需要開始一個痛苦的步驟:需要找到按下“激活”按鈕時所調用的函數。注意查看上圖右下的調用棧,由內向外依次嘗試對各個call下斷點(非條件斷點),每加一個斷點,嘗試讓UE運行,看是不是只是在點擊“激活”按鈕時才被斷下,如果不是,刪除斷點,繼續在上一級函數下斷點。嘗試幾次后,找到了一個call:0099B170。
第二步,分析判斷驗證碼/注冊碼匹配的關鍵指令邏輯。
關掉CheatEngine,使用IDA對UE可執行文件進行反匯編,並找到函數0099B170。由於文件較大,反匯編可能需要很久才能ready。
分析反匯編,發現下面指令是在判斷用戶是否輸入了許可證:
cmp dword ptr [eax-0Ch], 0 jnz loc_99B2F9
並且后續所調用的這一段邏輯與輸入了許可證但驗證錯誤的情況下都會被執行:
.text:0099B8FF loc_99B8FF: ; CODE XREF: sub_99B170+783j .text:0099B8FF mov edx, [eax] .text:0099B901 mov ecx, eax .text:0099B903 mov eax, [edx+0Ch] .text:0099B906 call eax .text:0099B908 lea edi, [eax+10h] .text:0099B90B mov [ebp+var_68], edi .text:0099B90E push 6D6Fh ; unsigned int .text:0099B913 mov byte ptr [ebp+var_4], 18h .text:0099B917 call ?AfxFindStringResourceHandle@@YGPAUHINSTANCE__@@I@Z ; AfxFindStringResourceHandle(uint) .text:0099B91C test eax, eax .text:0099B91E jz short loc_99B931 .text:0099B920 push 6D6Fh ; lpWideCharStr .text:0099B925 push eax ; hModule .text:0099B926 lea ecx, [ebp+var_80] .text:0099B929 call sub_4098F0 .text:0099B92E mov ebx, [ebp+var_80] .text:0099B931 .text:0099B931 loc_99B931: ; CODE XREF: sub_99B170+7AEj .text:0099B931 push 6D70h ; unsigned int .text:0099B936 call ?AfxFindStringResourceHandle@@YGPAUHINSTANCE__@@I@Z ; AfxFindStringResourceHandle(uint) .text:0099B93B test eax, eax .text:0099B93D jz short loc_99B950 .text:0099B93F push 6D70h ; lpWideCharStr .text:0099B944 push eax ; hModule .text:0099B945 lea ecx, [ebp+var_68]
由此猜測UE的驗證可能類似這樣的邏輯:
... ret = check(/*各種用戶的輸入*/) str_id = get_str_by_ret(ret); show_str(str_id) ...
另外有幸看到前面有一段這樣的指令:
.text:0099B1C4 push 0 ; bEnable .text:0099B1C6 mov ecx, eax .text:0099B1C8 call ?EnableWindow@CWnd@@QAEHH@Z ; CWnd::EnableWindow(int) .text:0099B1CD
使用ollydbg嘗試修改.text:0099B1C4為push 1,發現激活按鈕不再變灰,因此,猜測判斷注冊碼匹配與否的邏輯在上述兩段指令中間。
繼續分析ida的反匯編結果,發現中間有兩處調用atoi:
.text:0099B337 push ebx ; char * .text:0099B338 mov byte ptr [ebp+var_4], 4 .text:0099B33C call _atoi .text:0099B341 add esp, 4 .text:0099B344 push eax .text:0099B345 call sub_CCDA26 .text:0099B34A mov edi, eax .text:0099B34C mov eax, [ebp+var_6C] .text:0099B34F push 0C6h .text:0099B354 push eax ; char * .text:0099B355 call _atoi .text:0099B35A add esp, 4
ollydbg斷到這兩個地方,發現他們的入參分別的UE的驗證碼1和驗證碼2的字符串,可見,判斷注冊碼匹配與否的邏輯在這兩個atoi后面。
繼續分析ida的反匯編結果,發現在兩個atoi后面有一個這樣的指令:
.text:0099B363 lea ecx, [edi-13h] .text:0099B366 cmp ecx, 0Dh ; switch 14 cases .text:0099B369 ja loc_99B8B8 ; jumptable 0099B376 default case
感謝ida幫我們分析出這是一個switch case分支,猜測后面就是前面索要找的get_str_by_ret。
第三步,破解。
ollydbg嘗試修改執行cmp ecx, 0Dh前的ecx,最有可能正確的就是0,嘗試改為0,繼續運行,看到了彈出對話框,還可以試用30天。ollydbg脫離UE進程,停止調試,關閉UE,重新打開,沒有再彈出試用到期的提示,破解成功。
總結
其實最后一步破解時,算是運氣比較好的,比較容易地找到了判斷邏輯,如果嘗試失敗了,可能要繼續找到真正的check函數了。
網上其實有很多破解補丁可以下載,甚至有破解版的UE(但這個20.00.0.1040版本的沒找到),正如剛開始所說的,本文的意義在於記錄分享而非炫耀。
可以進一步延伸:
1、我想UE的剩余試用時間應該是寫在注冊表或者是某個文件中,如果可以找到的話,就有可能把試用時間改為無限長;
2、可以挑戰一下在線注冊,完全不同的思路呢;
3、就本文的思路,還差一個破解補丁,在cmp ecx, 0Dh之前修改一下ecx的值,這個也應該比較容易,后續有時間再搞。