日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Sublime Text 3143 Win32版本暴力破解过程

發(fā)布時間:2023/12/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Sublime Text 3143 Win32版本暴力破解过程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Sublime Text是一款強大的文本編輯器,在不注冊的情況下也可以使用,但標題欄的未注冊字樣與時不時彈出的nag窗口有時也讓人感覺很不爽,于是嘗試對其注冊過程進行分析與破解。

截至寫本篇文章時,Sublime Text的最新穩(wěn)定版本為3143版本。由于之前我們破解過其舊版本,所以對其注冊機制還算比較了解(已將之前破解Sublime Text 3126版本時寫的筆記整理成了博文,請見《Sublime Text 3126 Win32版本暴力破解過程》)。如果有心的話,大家不妨對比兩個版本的注冊機制和流程,看看從3126更新到3143版本這一年多的時間里,作者為了增大破解的難度做出了哪些努力。畢竟在攻與防的較量中,大家都在進步,我們在提高自己二進制代碼分析能力的同時,其實也有幸見證了軟件作者的成長。

一、第一次嘗試

在上個版本的Sublime中,我們很快就查到了注冊相關的邏輯,然后打開了局面。針對新版本,我們當然希望能夠故伎重演,就看作者給不給我們這個機會了。

首先隨意輸入一串注冊碼,點擊確定后彈出錯誤提示框,此時中斷程序,查看調(diào)用棧如下圖。

根據(jù)MessageBoxW的調(diào)用者地址即可回溯到Wrapper函數(shù)頭部。

從調(diào)用棧中可以看到再上幀的返回地址為0x00449817,為了保險,我們在Wrapper函數(shù)的頭部下斷后,重新輸入一次錯誤的注冊碼看看:

試圖繼續(xù)回溯時發(fā)現(xiàn)不是第一現(xiàn)場(ecx為0,而主調(diào)方是用ecx尋址的)

很明顯,這里把ecx清掉了。于是用IDA的交叉引用功能查到了調(diào)用源。emmmm,是JMP過去的,并且在之前清了ecx的值,作者也變猥瑣了。

繼續(xù)回溯:

分析發(fā)現(xiàn)ecx像是一個結(jié)構(gòu)指針,其中第二個位置放的就是函數(shù)指針,第三個位置放的是傳入的參數(shù)。于是利用函數(shù)指針地址下條件斷點。顯示從ecx開始,[ECX+4]==0x0044F4E0,在下面代碼中注釋部分標注了下斷點的方法,由于ecx的值是通過esi取到的,于是[ESI+4]==0x0044F4E0,然后esi又是通過eax取到的,于是又更進下[[EAX+8]+4]==0x0044F4E0。果然這些條件斷點都被命中了。

.text:00589A1D loc_589A1D: .text:00589A1D 8B 70 08 mov esi, [eax+8] ;[[EAX+8]+4]==0x0044F4E0 .text:00589A20 8D 48 10 lea ecx, [eax+10h] .text:00589A23 50 push eax .text:00589A24 E8 B7 62 00 00 call sub_58FCE0 .text:00589A29 83 2D F8 30 8A+sub dword_8A30F8, 10h .text:00589A30 8B 06 mov eax, [esi] .text:00589A32 59 pop ecx .text:00589A33 8B CE mov ecx, esi ;[ESI+4]==0x0044F4E0 .text:00589A35 FF 50 04 call dword ptr [eax+4] ;[ECX+4]==0x0044F4E0 .text:00589A38 8B 06 mov eax, [esi] .text:00589A3A 8B CE mov ecx, esi .text:00589A3C 6A 01 push 1 .text:00589A3E FF 10 call dword ptr [eax] .text:00589A40 E8 3D 6B 00 00 call sub_59058

OD中在這句上下斷:

00589A1D >|> 8B70 08 mov esi,dword ptr ds:[eax+0x8]

再來一次,斷下后Ctrl+A分析一下程序,程序就顯示出了跳轉(zhuǎn)的來源,這里十分不巧,有兩點,都下條件斷判定一下:

再來一次,嗯,是從下面跳上來的:

此時eax中的值是關鍵,當前為0x03DCA1C0,應該是堆棧的地址。那是什么時候取出來的呢?

哇,全局變量來了,咱們終于落地了。只要有了全局變量,一切都好商量。

哈哈,全局就只有一處可以設值的,其他三處都是改寫的。
大膽猜測,現(xiàn)在所在的函數(shù)就是從消息到消息響應函數(shù)的映射函數(shù),而目標函數(shù)就是負責查出二者映射關系的函數(shù)。

結(jié)合IDA發(fā)現(xiàn)這些函數(shù)是通過消息循環(huán)被調(diào)用的:

WPARAM sub_58BA83() {signed int v0; // esi_DWORD *v1; // eaxbool v2; // blint v3; // ecx_DWORD *v4; // eaxbool v5; // blMSG Msg; // [esp+Ch] [ebp-64h]char v8; // [esp+28h] [ebp-48h]char v9; // [esp+30h] [ebp-40h]char v10; // [esp+38h] [ebp-38h]char v11; // [esp+40h] [ebp-30h]int v12; // [esp+48h] [ebp-28h]_DWORD *v13; // [esp+50h] [ebp-20h]WPARAM v14; // [esp+58h] [ebp-18h]int v15; // [esp+6Ch] [ebp-4h]v14 = -1;MsgHandler(); LABEL_2:Msg.hwnd = 0;memset(&Msg.message, 0, 0x18u);v0 = 0;while ( 1 ){if ( !PeekMessageW(&Msg, 0, 0, 0, 1u) ){sub_58EFBC(&v13);v15 = 0;while ( 1 ){v1 = (_DWORD *)sub_58EF7A(&v11);v2 = v13 != (_DWORD *)*v1;sub_452FC0(&v11);if ( !v2 )break;sub_589E11(*v13);sub_452FA3(&v9);sub_452FC0(&v9);}v15 = -1;sub_452FC0(&v13);sub_590582();if ( v0 == 2 )MsgHandler();if ( !PeekMessageW(&Msg, 0, 0, 0, 0) ){sub_58EFBC(&v12);v15 = 1;while ( 1 ){v4 = (_DWORD *)sub_58EF7A(&v10);v5 = v12 != *v4;sub_452FC0(&v10);if ( !v5 )break;v3 = *(_DWORD *)(*(_DWORD *)v12 + 4);if ( v3 )(*(void (**)(void))(*(_DWORD *)v3 + 24))();sub_452FA3(&v8);sub_452FC0(&v8);}v15 = -1;sub_452FC0(&v12);sub_590582();MsgWaitForMultipleObjectsEx(0, 0, 0xFFFFFFFF, 0x1CFFu, 6u);}goto LABEL_2;}if ( Msg.message == 18 )break;switch ( Msg.message ){case 0x7E9u:if ( v0 ){if ( v0 == 1 )v0 = 2;}else{MsgHandler();v0 = 1;}break;case 0x7EAu:v14 = Msg.wParam;break;case 0x7EBu:return v14;default:if ( (Msg.message == 256 || Msg.message == 257 || Msg.message == 260 || Msg.message == 261)&& sub_589BFD(Msg.hwnd) ){if ( sub_58A100(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam) != 0 )TranslateMessage(&Msg);}else{TranslateMessage(&Msg);DispatchMessageW(&Msg);}sub_590582();break;}}PostQuitMessage(Msg.wParam);return v14; }

可以看到,Sublime的作者自定義了0x7E9消息,在其中完成了錯誤對話框的顯示。并且經(jīng)過分析,該消息至少還有如下兩種觸發(fā)方式:
1. 作者設置的定時器會調(diào)用MsgHandler函數(shù)
2. 該函數(shù)還負責處理其他窗口消息,如窗口獲得焦點等等

因此,在該函數(shù)處下斷或在SendMessage/PostMessage等函數(shù)上下斷都會有較大的工作量,意味著我們要從眾多干擾信息中找出真正關注的目標信息。并且即便找到了發(fā)送該消息的真身,也可能并不意味著我們找到了注冊相關的邏輯。因為作者完全可以在二者之間再采用一些猥瑣的方法增加熵值。基于上述分析,我決定放棄這種思路。

第二次嘗試

Sublime的未注冊版本中有一個nag窗口,是在用戶保存文件到一定次數(shù)時觸發(fā)的。依然采用下斷MessageBoxW的方式,配合棧回溯和IDA靜態(tài)分析,結(jié)果又來到了上面的0x7E9消息處理函數(shù)中。
這里就不再截圖了,因為大部分圖都和上面類似。

新思路

在思路二中,我們其實想利用未注冊版Sublime的一些提示信息,如保存時的nag窗口、關于窗口中的未注冊等等、標題欄的未注冊字樣。既然nag窗口走不通,我們就試試后面的吧。

搜索字符串:

放到IDA里看看流程圖:

可以看到一個典型的分支結(jié)構(gòu)。并且判斷的條件是將一個結(jié)構(gòu)的第一字節(jié)是否為0,如果為0則進入了未注冊分支;且該結(jié)構(gòu)為全局結(jié)構(gòu)。因此可對其下硬件訪問斷點:

hr 0x008845E8

先暫時不忙啟用該斷點,而是在注冊對話框中輸入了key后啟用該斷點,然后確認,程序斷在如下位置:

回溯即找到0x45075D的函數(shù),依然是IDA結(jié)合OD的方法:

.text:0045075D B8 EC 97 76 00 mov eax, offset loc_7697EC .text:00450762 E8 69 1B 31 00 call __EH_prolog .text:00450767 81 EC 8C 00 00+sub esp, 8Ch .text:0045076D 53 push ebx .text:0045076E 56 push esi .text:0045076F 57 push edi .text:00450770 8B F9 mov edi, ecx .text:00450772 8D 8D 68 FF FF+lea ecx, [ebp+lpszKeyUnicode] .text:00450778 51 push ecx .text:00450779 8B 87 E0 01 00+mov eax, [edi+1E0h] .text:0045077F 8B 80 B4 00 00+mov eax, [eax+0B4h] .text:00450785 8B 88 80 03 00+mov ecx, [eax+380h] .text:0045078B E8 3F 9D 04 00 call GetKey .text:00450790 33 DB xor ebx, ebx .text:00450792 8D 4D D4 lea ecx, [ebp+lpszKey] .text:00450795 8B D0 mov edx, eax .text:00450797 ; try { .text:00450797 89 5D FC mov [ebp+var_4], ebx .text:0045079A E8 C6 28 07 00 call ConvertKeyToASCII .text:0045079F 53 push ebx .text:004507A0 6A 01 push 1 .text:004507A2 8D 8D 68 FF FF+lea ecx, [ebp+lpszKeyUnicode] .text:004507A2 FF ; } // starts at 450797 .text:004507A8 ; try { .text:004507A8 C6 45 FC 02 mov byte ptr [ebp+var_4], 2 .text:004507AC E8 0C 77 FC FF call FreeString .text:004507B1 8B 8F D8 01 00+mov ecx, [edi+1D8h] ; 這里取到了注冊結(jié)構(gòu) .text:004507B7 6A 0F push 0Fh .text:004507B9 58 pop eax .text:004507BA 89 45 9C mov [ebp+var_64], eax .text:004507BD 89 45 B4 mov [ebp+var_4C], eax .text:004507C0 8D 45 80 lea eax, [ebp+pOrgRegInfo] .text:004507C3 50 push eax .text:004507C4 66 89 5D 80 mov [ebp+pOrgRegInfo], bx ; 這里ebx是0,因此是在清空該結(jié)構(gòu)的各字段 .text:004507C8 89 5D 84 mov [ebp+var_7C], ebx .text:004507CB 89 5D 98 mov [ebp+var_68], ebx .text:004507CE 88 5D 88 mov [ebp+var_78], bl .text:004507D1 89 5D B0 mov [ebp+var_50], ebx .text:004507D4 88 5D A0 mov [ebp+var_60], bl .text:004507D7 E8 60 EF FF FF call CopyRegInfo ; 這里將清空后的注冊結(jié)構(gòu)拷貝給全局的注冊結(jié)構(gòu) .text:004507DC 53 push ebx ; size_t .text:004507DD 6A 01 push 1 ; char .text:004507DF 8D 4D A0 lea ecx, [ebp+var_60] ; void * .text:004507E2 E8 E2 56 FB FF call sub_405EC9 .text:004507E7 53 push ebx ; size_t .text:004507E8 6A 01 push 1 ; char .text:004507EA 8D 4D 88 lea ecx, [ebp+var_78] ; void * .text:004507ED E8 D7 56 FB FF call sub_405EC9 .text:004507F2 39 5D E4 cmp [ebp+var_1C], ebx .text:004507F5 0F 84 C4 01 00+jz loc_4509BF .text:004507FB 8B 97 D8 01 00+mov edx, [edi+1D8h] ;這里取出了全局結(jié)構(gòu) .text:00450801 8D 4D D4 lea ecx, [ebp+lpszKey] .text:00450804 8D 42 01 lea eax, [edx+1] .text:00450807 50 push eax .text:00450808 8D 45 EC lea eax, [ebp+lpParameter] .text:0045080B 50 push eax .text:0045080C 8D 42 04 lea eax, [edx+4] .text:0045080F 83 C2 08 add edx, 8 .text:00450812 50 push eax .text:00450813 E8 F6 F1 FF FF call CheckKey .text:00450818 8B 8F D8 01 00+mov ecx, [edi+1D8h] ; 取出全局結(jié)構(gòu) .text:0045081E 83 C4 0C add esp, 0Ch .text:00450821 8B F0 mov esi, eax .text:00450823 83 FE 01 cmp esi, 1 .text:00450826 0F 94 C2 setz dl .text:00450829 88 11 mov [ecx], dl .text:0045082B 8B 8F D8 01 00+mov ecx, [edi+1D8h] .text:00450831 38 19 cmp [ecx], bl .text:00450833 74 13 jz short loc_450848

其中,CheckKey函數(shù)在注冊碼正確時應該返回1,緊接著就是對該返回值的判斷:

可以看到,注冊失敗返回的是2。考慮到程序中可能還有其他地方調(diào)用注冊驗證函數(shù),我們修改其頭部代碼,讓其直接返回1即可:
下面是原驗證函數(shù)的入口:

修改以后變成了這樣:

此外,通過IDA的靜態(tài)分析結(jié)合OD動態(tài)調(diào)試,在注冊流程下面還開啟了一個線程向服務器報告注冊情況:

對應的線程是由下面這段代碼開啟的。

.text:00450938 .text:00450938 loc_450938: ; lpThreadId .text:00450938 53 push ebx .text:00450939 53 push ebx ; dwCreationFlags .text:0045093A FF 75 EC push [ebp+lpParameter] ; lpParameter .text:0045093D 68 73 F7 44 00 push offset sub_44F773 ; lpStartAddress .text:00450942 53 push ebx ; dwStackSize .text:00450943 53 push ebx ; lpThreadAttributes .text:00450944 FF 15 1C 61 78+call ds:CreateThread .text:0045094A 50 push eax ; hObject .text:0045094B FF 15 3C 63 78+call ds:CloseHandle .text:00450951 8B 87 D8 01 00+mov eax, [edi+1D8h] .text:00450957 6A 0C push 0Ch .text:00450959 80 78 01 00 cmp byte ptr [eax+1], 0 .text:0045095D 74 08 jz short loc_450967

在OD中查看如下:

本著不打擾作者也不被作者打擾的考量將其干掉:

首先干掉線程函數(shù),直接使用頭部ret大法即可。注意這里是__stdcall調(diào)用約定,線程函數(shù)傳入一個lpParameter,因此應該為ret4.

下面再把負責CreateThread的那片代碼也干掉:

雖然將傳參、壓棧過程全部用0x90填充了,但是由于CreateThread的某些參數(shù)(如函數(shù)入口)在可執(zhí)行程序加載時涉及到重定位,因此還是直接用一個短跳轉(zhuǎn)略過這一片比較好。至此,sublime破解完成。隨意輸入序列號即可完成注冊。

在啟動過程中,Sublime也會發(fā)送注冊信息到驗證服務器:

于是將其啟動時連網(wǎng)進行版本檢查的代碼也干掉。還是在InternetOpenW處下斷點,然后回溯到調(diào)用處:0x005A750D,進而回溯到函數(shù)頭部,使用ret大法。

讓程序啟動即為破解版

對于懶人來說,連手動注冊這一步都希望能省掉。根據(jù)前面的分析,我們知道程序是否注冊是由一個全局結(jié)構(gòu)來控制的。因此在啟動過程中對該全局地址下硬件訪問斷點。看該地址中的值何時被讀取:
在OD中設置硬件訪問斷點:hr 0x008845E8,然后重新運行程序。
很快斷點命中:

可以看出,此時全局結(jié)構(gòu)中注冊標志為0,而這里取出了該標志與0判斷。因此我們將其改為
or byte ptr [eax], 1

這樣無論如何執(zhí)行完該語句后,該結(jié)構(gòu)中的值都為1了。再次運行程序,直接就成為了已注冊版。至此破解完畢。

總結(jié)

以上是生活随笔為你收集整理的Sublime Text 3143 Win32版本暴力破解过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。