繼上篇那個(gè)VB后,我也想做個(gè)C/C++類的例子,正好看到FreeSoul寫的FScrackme2,所以寫了這篇文章。
FScrackme2應(yīng)該是在lunix下取了GNU中的 GCC.exe 和 G++.exe來編譯的一個(gè)C程序! 所以用PEID來看語(yǔ)言類型是MingWin32 GCC 3.x。也就是GCC編譯的C程序。
先來分析程序,找到真正的注冊(cè)碼并不難,OD中的算法部分:
00401AE7 |. 8945 9C MOV [LOCAL.25],EAX ; |
00401AEA |. 837D A0 03 CMP [LOCAL.24],3 ; |用戶名長(zhǎng)度必須大于3位
00401AEE |. 0F8E C2000000 JLE <FScrackm.loc_401BB6> ; |
00401AF4 |. 837D 9C 1A CMP [LOCAL.25],1A ; |注冊(cè)碼長(zhǎng)度必須大于27位
00401AF8 |. 0F8E B8000000 JLE <FScrackm.loc_401BB6> ; |
...
00401B62 |. 8B45 9C MOV EAX,[LOCAL.25]
00401B65 |. 894424 0C MOV DWORD PTR SS:[ESP+C],EAX ; 注冊(cè)碼長(zhǎng)度
00401B69 |. 8B45 A0 MOV EAX,[LOCAL.24]
00401B6C |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX ; 用戶名長(zhǎng)度
00401B70 |. 8B45 A4 MOV EAX,[LOCAL.23]
00401B73 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; 注冊(cè)碼ASCII
00401B77 |. 8B45 EC MOV EAX,[LOCAL.5]
00401B7A |. 890424 MOV DWORD PTR SS:[ESP],EAX ; 用戶名ASCII
00401B7D |. E8 1C010000 CALL <FScrackm.sub_401C9E> ; 主要算法
00401B82 |. 85C0 TEST EAX,EAX
00401B84 |. 75 18 JNZ SHORT <FScrackm.loc_401B9E> ; 判斷返回值是否為0
================================================================================================
CALL <FScrackm.sub_401C9E> 主要算法:
00401D33 > > \8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00401D36 . 8038 4E CMP BYTE PTR DS:[EAX],4E ; 對(duì)比第一位注冊(cè)碼是否為“N”
00401D39 . 74 0F JE SHORT <FScrackm.loc_401D4A>
00401D3B . C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D45 . E9 7E070000 JMP <FScrackm.loc_4024C8>
00401D4A > > 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00401D4D . 40 INC EAX
00401D4E . 8038 61 CMP BYTE PTR DS:[EAX],61 ; 對(duì)比第二位注冊(cè)碼是否為“a”
00401D51 . 74 07 JE SHORT <FScrackm.loc_401D5A>
00401D53 . C745 94 00000>MOV DWORD PTR SS:[EBP-6C],0
00401D5A > > 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00401D5D . 83C0 02 ADD EAX,2
00401D60 . 8038 52 CMP BYTE PTR DS:[EAX],52 ; 對(duì)比第三位注冊(cè)碼是否為“R”
00401D63 . 74 0F JE SHORT <FScrackm.loc_401D74>
00401D65 . C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D6F . E9 54070000 JMP <FScrackm.loc_4024C8>
00401D74 > > 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00401D77 . 83C0 03 ADD EAX,3
00401D7A . 8038 46 CMP BYTE PTR DS:[EAX],46 ; 對(duì)比第四位注冊(cè)碼是否為“F”
00401D7D . 74 0F JE SHORT <FScrackm.loc_401D8E>
00401D7F . C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D89 . E9 3A070000 JMP <FScrackm.loc_4024C8>
...
00401E8B . 83C0 04 ADD EAX,4
00401E8E . 0FB600 MOVZX EAX,BYTE PTR DS:[EAX]
00401E91 . 3A45 87 CMP AL,BYTE PTR SS:[EBP-79] ; 對(duì)比第5位開始5位注冊(cè)碼
00401E94 . 74 0F JE SHORT <FScrackm.loc_401EA5>
...
00401EEA . 83C1 09 ADD ECX,9
00401EF6 . 0FB601 MOVZX EAX,BYTE PTR DS:[ECX]
00401EF9 . 3A02 CMP AL,BYTE PTR DS:[EDX] ; 對(duì)比第10位注冊(cè)碼
00401EFB . 74 0F JE SHORT <FScrackm.loc_401F0C>
...
00401F4A . 83C2 0A ADD EDX,0A
00401F4D . 8B85 70FFFFFF MOV EAX,DWORD PTR SS:[EBP-90] ; 對(duì)比第11位注冊(cè)碼...
00401F8B . E8 30420000 CALL <FScrackm.isalpha> ; 第12位后6位注冊(cè)碼,必須為字母
..
00401FB5 . E8 F6410000 CALL <FScrackm.isupper> ; 第12位后6位注冊(cè)碼,必須大寫
...
0040205F . 83C0 0A ADD EAX,0A ; 第10位后
00402075 . 0FBEC0 MOVSX EAX,AL
00402078 . 39C1 CMP ECX,EAX ; 對(duì)比第11位開始3位的注冊(cè)碼
0040207A . 74 0F JE SHORT <FScrackm.loc_40208B>
...
004020B1 . 0FBEC0 MOVSX EAX,AL
004020B4 . 39C1 CMP ECX,EAX ; 對(duì)比第17位開始倒數(shù)3位的注冊(cè)碼
004020B6 . 74 44 JE SHORT <FScrackm.loc_4020FC>
...
00402125 . 83C0 11 ADD EAX,11 ; 第17位后
00402128 . 0FBE00 MOVSX EAX,BYTE PTR DS:[EAX]
0040212B . 890424 MOV DWORD PTR SS:[ESP],EAX
0040212E . E8 6D400000 CALL <FScrackm.isdigit> ; \第18位開始的6位必須為數(shù)字
00402133 . 85C0 TEST EAX,EAX
00402135 . 75 0F JNZ SHORT <FScrackm.loc_402146>
...
0040220D . 0FB601 MOVZX EAX,BYTE PTR DS:[ECX]
00402210 . 3A02 CMP AL,BYTE PTR DS:[EDX] ; 對(duì)比第18位后的6位注冊(cè)碼
00402212 . 74 0F JE SHORT <FScrackm.loc_402223>
...
00402230 . 83C0 17 ADD EAX,17
00402233 . 8038 2D CMP BYTE PTR DS:[EAX],2D ; 對(duì)比第24位注冊(cè)碼,必須為“-”號(hào)
00402236 . 74 07 JE SHORT <FScrackm.loc_40223F>
...
004022C7 . 83C1 18 ADD ECX,18
004022CA . 8B85 58FFFFFF MOV EAX,DWORD PTR SS:[EBP-A8]
004022D0 . 0FBE10 MOVSX EDX,BYTE PTR DS:[EAX]
004022D3 . 0FB601 MOVZX EAX,BYTE PTR DS:[ECX]
004022D6 . 3A842A 78FFFF>CMP AL,BYTE PTR DS:[EDX+EBP-88] ; 對(duì)比第25位注冊(cè)碼
004022DD . 74 0F JE SHORT <FScrackm.loc_4022EE>
...
00402474 . 83C2 19 ADD EDX,19
00402477 . 8B85 50FFFFFF MOV EAX,DWORD PTR SS:[EBP-B0]
0040247D . 3802 CMP BYTE PTR DS:[EDX],AL ; 對(duì)比第26位注冊(cè)碼
0040247F . 74 07 JE SHORT <FScrackm.loc_402488>
00402481 . C745 94 00000>MOV DWORD PTR SS:[EBP-6C],0
00402488 > > 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
0040248B . 83C0 1A ADD EAX,1A
0040248E . 8038 44 CMP BYTE PTR DS:[EAX],44 ; 對(duì)比第27位注冊(cè)碼是否為“D”
00402491 . 74 07 JE SHORT <FScrackm.loc_40249A>
...
0040249A > > \837D 14 1B CMP DWORD PTR SS:[EBP+14],1B ; 對(duì)比注冊(cè)碼長(zhǎng)度是否為27位
0040249E . 74 0C JE SHORT <FScrackm.loc_4024AC>
==================================================================================================
我們主要是討論寫注冊(cè)機(jī),所以算法具體不去討論。我們現(xiàn)在所知道的是,用戶名要大于三位,注冊(cè)碼必須為27位,而且我們知道了每個(gè)注冊(cè)碼比較的地方。CALL 00401C9E里面是具體的算法,里面還有很多子函數(shù),想提取這樣一個(gè)含有很多子程序的代碼到注冊(cè)機(jī)里在OD中好象沒有好的方法,只有手動(dòng)提,而在IDA中就有辦法,這得益于最早由dreaman寫的提取函數(shù)的腳本,我稱它為GetCall.IDC(http://bbs.pediy.com/showthread.php?s=&threadid=23231),在IDA中,只要將光標(biāo)定位于一個(gè)函數(shù)的起始代碼,運(yùn)行腳本后,它會(huì)截取所有該函數(shù)的代碼,包括里面所有的子函數(shù)代碼,并在文件目錄保存匯編代碼為一個(gè)ASM文件。真是太方便了。讓我們行動(dòng)。
IDA反匯編FScrackme2,“G”到401C9E,菜單“文件”-“IDC文件”,運(yùn)行腳本。FScrackme2目錄中已經(jīng)生成了FScrackme2Part.asm。但是在這之前,對(duì)于這個(gè)軟件,需要在IDA中動(dòng)動(dòng)手腳。因?yàn)檐浖荊CC寫的,IDA的腳本gcc32rtf.sig自動(dòng)把GCC的庫(kù)函數(shù)識(shí)別出來了:
.text:00401DCF call __Znaj
這本來是個(gè)好事,我也想找GCC的運(yùn)行庫(kù),然后將它轉(zhuǎn)換成MASM的庫(kù)文件,這樣,這些CALL就可以直接在MASM中引用了,寫注冊(cè)機(jī)就非常方便了。但是找不到這樣的運(yùn)行庫(kù)。而GetCall.IDC腳本只要碰到被IDA識(shí)別出來的函數(shù),它就不進(jìn)去拷里面的子函數(shù),所以,解決方法只有一個(gè),將IDA中的SIG文件夾下的gcc32rtf.sig文件刪除,再讓IDA反匯編FScrackme2,再拷CALL 401C9E里的代碼。這樣拷出來的的子函數(shù)就比較全了。(當(dāng)然我也把所有的GCC的庫(kù)函數(shù)提取出來做個(gè)備份,誰(shuí)需要的話跟我聯(lián)系。)
現(xiàn)在就已經(jīng)把代碼全部拷出到FScrackme2Part.asm了,我們不管三七二十一先把這段匯編代碼拷到一個(gè)匯編注冊(cè)機(jī)模板里再說,就是上次那個(gè)模板,當(dāng)然這樣在RADASM中運(yùn)行,錯(cuò)誤會(huì)非常多,有錯(cuò)就改嘛,中國(guó)人的好作風(fēng)。
因?yàn)槭荂語(yǔ)言類的,所以不可避免的用到C運(yùn)行庫(kù)msvcrt.dll。所以看到匯編代碼中很多對(duì)msvcrt的調(diào)用,比如:
.text:00401F8B call isalpha
.text:00401D14 call isalnum
先解決這個(gè)問題。有了上篇的文章,這已經(jīng)不是問題了,用Dll2inc將msvcrt.dll轉(zhuǎn)換為MASM的msvcrt.lib和msvcrt.inc文件,然后在匯編代碼中調(diào)用這兩個(gè)文件。這樣就可以直接在匯編中調(diào)用msvcrt的庫(kù)函數(shù)了。
然后繼續(xù)讓RADASM找出錯(cuò)誤,一步一步來解決,一般的方法是將RADASM中的錯(cuò)誤提示地址復(fù)制出來,在IDA中G到該地址,看數(shù)據(jù)是屬于哪個(gè)段的,如果是.text可執(zhí)行代碼段的子函數(shù),就用GetCall腳本復(fù)制出來粘貼到匯編代碼區(qū),如果是.bss、.rdata、.data 等數(shù)據(jù)段,就拷貝到匯編代碼的.DATA區(qū),LINUX中還會(huì)出現(xiàn).stab .stabstr .comment .note .shstrtab .symtab .strtab,不過這些都沒用。我想在下篇結(jié)束篇專門寫如何從IDA中拷代碼,結(jié)合具體實(shí)例講的詳細(xì)些。在這里,拷好的代碼就看附件中的注冊(cè)機(jī)代碼。
拷好并修改好后這儼然是用匯編代碼仿制FScrackme2,但是文件大小比原來小了N倍,執(zhí)行速度也快了不少。
做成個(gè)仿制FScrackme2還不行,我們是要做它的注冊(cè)機(jī)。
很多crackme都可以通過用戶名算出個(gè)結(jié)果,然后將注冊(cè)碼也通過運(yùn)算得到這個(gè)結(jié)果,那就可以通過注冊(cè)碼算法的逆運(yùn)算推出真實(shí)的注冊(cè)碼。
但是這個(gè)crackme是個(gè)正向的,它就是通過運(yùn)算得到一個(gè)個(gè)注冊(cè)碼,然后將輸入的注冊(cè)碼也一一對(duì)比。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
對(duì)于這種crackme寫注冊(cè)機(jī)時(shí)我提供一種解決思路,就是制造一個(gè)“偽注冊(cè)碼”,然后將程序里算出的一個(gè)個(gè)真實(shí)的注冊(cè)碼重新存到一個(gè)地方,這個(gè)地方的字符串就是真的注冊(cè)碼了。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
首先說偽注冊(cè)碼,只要符合條件的任何字符都行。我這里為了減少?gòu)澛罚鶕?jù)軟件的要求:第12到17位必須為大寫字母,第18位和第24位必須為數(shù)字,我提供個(gè)名稱為TEMP的偽注冊(cè)碼:12345678901ABCDEF123456789D:
.data
Temp db '12345678901ABCDEF123456789D',0
我將一個(gè)個(gè)真實(shí)的注冊(cè)碼存到SerialBuffer中:
invoke SetDlgItemText,hDlg,IDC_SERIAL,addr SerialBuffer
其次要將所有對(duì)比注冊(cè)碼的地方都改為移動(dòng)真實(shí)的注冊(cè)碼到SerialBuffer,并且將那些對(duì)比發(fā)生錯(cuò)誤跳轉(zhuǎn)到退出的地方都“爆掉”,相當(dāng)于先爆破再?gòu)?fù)制代碼:
cmp byte ptr [eax], 4Eh ;對(duì)比第一位注冊(cè)碼是否為“N”
mov [SerialBuffer+0],4Eh ;將注冊(cè)碼第一位“N”寫回
jmp short loc_401D4A ;避免退出,原來是je 401D4A
再次要注意原來程序中循環(huán)取輸入的注冊(cè)碼進(jìn)行對(duì)比的地方,我們要將它改為循環(huán)存儲(chǔ)到SerialBuffer中,這就需要個(gè)累計(jì)參數(shù),可以利用原程序中循環(huán)的時(shí)候?qū)Ρ仁欠裱h(huán)完的那個(gè)參數(shù),用那個(gè)非常爽:
mov [ebp+var_8C], 1
…
cmp [ebp+var_8C], 6 ;是否取完
….
mov al, [edx] ;對(duì)比第18位后的6位注冊(cè)碼
xor ecx, ecx
mov cl, byte ptr [ebp+var_8C] ;循環(huán)中的對(duì)比參數(shù)
mov [SerialBuffer+ecx+10h],al ;寫回第18位后的6位注冊(cè)碼
jmp short loc_402223 ;避免退出
mov [ebp+var_B4], 1
jmp loc_4024C8 ;跳到退出
…
lea eax, [ebp+var_8C]
inc dword ptr [eax] ;累計(jì)
最后還有一點(diǎn),就是用前面已經(jīng)計(jì)算出來的正確注冊(cè)碼來推算后面部分的注冊(cè)碼,這時(shí),不能偽注冊(cè)碼來推算了,因?yàn)樗旧硎清e(cuò)誤的,所以推算出來的后面部分也是錯(cuò)的,要替換成真實(shí)的注冊(cè)碼,如:
mov eax, [ebp+arg_4] ;注意這里,這里是將已算出的注冊(cè)碼第23位拿來運(yùn)算,所以要改成:
mov eax, offset SerialBuffer ;將已經(jīng)算出的注冊(cè)碼放到EAX,當(dāng)然也包括已算出的第23位
mov [esp+0C8h+var_C8], eax
call sub_4024FE
然后我們來看看原來程序取用戶名和注冊(cè)碼的過程,我們可以替換成取用戶名和偽注冊(cè)碼:
00401B65 |. 894424 0C MOV DWORD PTR SS:[ESP+C],EAX ; 注冊(cè)碼長(zhǎng)度
00401B69 |. 8B45 A0 MOV EAX,[LOCAL.24]
00401B6C |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX ; 用戶名長(zhǎng)度
00401B70 |. 8B45 A4 MOV EAX,[LOCAL.23]
00401B73 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; 注冊(cè)碼ASCII
00401B77 |. 8B45 EC MOV EAX,[LOCAL.5]
00401B7A |. 890424 MOV DWORD PTR SS:[ESP],EAX ; 用戶名ASCII
00401B7D |. E8 1C010000 CALL <FScrackm.sub_401C9E> ; 主要算法
00401B82 |. 85C0 TEST EAX,EAX
00401B84 |. 75 18 JNZ SHORT <FScrackm.loc_401B9E> ; 判斷返回值是否為0
只要改為:
invoke lstrlen, addr Temp ;取偽注冊(cè)碼長(zhǎng)度
mov [esp+98h+var_8C], eax
invoke lstrlen, addr NameBuffer ;取用戶名長(zhǎng)度
mov [esp+98h+var_90], eax
mov [esp+98h+var_94], offset Temp ;裝入偽注冊(cè)碼
mov [esp+98h+var_98], offset NameBuffer ;裝入用戶名
call sub_401C9E ;調(diào)用算法CALL
test eax, eax
jnz loc_401B9E
我們還可以利用原來注冊(cè)碼錯(cuò)誤的對(duì)話框的地方來提示對(duì)用戶名各種限制,比如要求第一位和最后一位必須是字母等。
這樣弄好后,到最后一位注冊(cè)碼算好,所有的真實(shí)注冊(cè)碼也就已經(jīng)全部存到SerialBuffer中了。
如果還有錯(cuò)誤,最好的辦法就是在RADASM中用OD調(diào)試跟蹤錯(cuò)誤。
注冊(cè)機(jī)的源代碼我已經(jīng)放在附件里,最好結(jié)合原程序看看。注冊(cè)機(jī)里我已經(jīng)做了比較詳細(xì)的注釋了。
注冊(cè)機(jī)中的全部代碼都拷自FScrackme2在IDA中反匯編后的代碼,也就是幾乎全部是算法函數(shù)CALL 401C9E里的代碼。我盡量保持“原汁原味”,雖然有些代碼完全可以刪除,但是直接拷貝比判斷該刪除哪些要好。
主要就是加入了存儲(chǔ)真實(shí)注冊(cè)碼的過程,看看每個(gè)注冊(cè)碼的存儲(chǔ)方法,慢慢體會(huì)。
附件:單擊下載
【版權(quán)聲明】: 本文原創(chuàng)于看雪技術(shù)論壇, 轉(zhuǎn)載請(qǐng)注明作者并保持文章的完整, 謝謝!
2006年12月24日