查看單個文章
舊 2006-02-15, 04:48 PM   #14 (permalink)
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設

尋找軟體的註冊碼

第七章-尋找軟體的註冊碼
我們來尋找軟體真正的註冊碼!
尋找軟體的註冊碼就像你小時玩的躲貓貓一樣,簡單又有趣,雖然後來你會不這樣覺的
好的,我們開始。
我不知道你有沒有明白我前面在原理中講的那些東西,如果沒明白,我就再說一遍
軟體通過你輸入的用戶名或者機器碼什麼的產生一個正確的註冊碼來與你輸入的註冊碼進行比較,如果兩個相同,也就是說你輸入的註冊碼是正確的話,那軟體就會完成註冊。如果你輸入的不正確,嘿嘿,當然就不會註冊成功。
好的,現在你已經知道軟體會有一個比較兩個註冊碼的程序,這就是關鍵所在。一般如果你遇到的是那種明碼比較的軟體,這會是一件非常另人愉快的事情的
軟體會先計算出正確的註冊碼然後再與你輸入的那個進行比較,也就是說正確的註冊碼會被軟體自己算出來!嘿嘿,搜身你會嗎?雖然法律以及道德不允許我們去搜身,但…
我接著說,雖然現在的軟體已經比以前要厲害上許多,但,那種用明碼比較的,還是大有人在的。所謂明碼比較,就是說軟體先算出正確的註冊碼,然後放到記憶體或你家的「我是頭號大傻瓜」下面,之後再得到你輸入的那個註冊碼,接著就比較了。哈哈,好理解吧,我們只要找到那個比較的地方,看一下軟體把註冊碼放到記憶體的哪裡了,再到相應的記憶體處瞧一瞧,就萬事OK了!
還記的對你說過的那些一般的(也是最菜的)比較嗎?我撿其中最簡單的一個來給你再解釋一下:
mov eax [ ] 這裡可以是位址,也可以是其它暫存器 該條指令也可以是mov eax [ ]
mov edx [ ] 同上 通常這兩個位址就儲存著重要訊息 該指令也可以是 pop edx
call 00?????? 關鍵call
jz(jnz)或 jne(je) 關鍵跳轉
第一條mov eax [ ]指令是將一個記憶體位址或另外一個暫存器(該暫存器中裝的是記憶體位址)裝入eax中。第二條指令與其相同,是將一個記憶體位址或另外一個暫存器中的記憶體位址裝入edx中。而這兩條指令是幹什麼的呢?嘿嘿嘿嘿…
這兩條指令就是用來存放真假兩個註冊碼的位址的,也就是說eax和edx這兩個暫存器中此時一個裝的是正確的註冊碼的記憶體位址,一個是你輸入的那個錯誤的註冊碼的記憶體位址。軟體在比較註冊碼前將兩個註冊碼的記憶體位址分別裝入到兩個暫存器中,然後就是關鍵Call出場。在這個關鍵Call中對註冊碼進行比較時,軟體會從相應的暫存器中取出兩個註冊碼來比較,接著出來就是一個關鍵跳轉,通過上面Call中的比較結果來做相應的跳轉…
你應該已經想到什麼了吧!沒錯,我們只要找到軟體的關鍵Call,然後在關鍵Call處來檢視相應的記憶體位址就可以找到正確的註冊碼了 而這一切,都可以通過偵錯器來完成。從某種意義上來說,如果你能自己一個人把你家的微波爐修好,那你就絕對會用偵錯器 我們在偵錯器中,只要一步一步執行到關鍵Call處,然後用d eax和d edx就可以檢視兩個位址中放的兩個註冊碼,如果你發現其中的一個是你自己剛才輸入的,那麼另一條就是正確的
而所謂的記憶體註冊機呢?我這裡就不再多說了,它的原理就是自動在軟體註冊的時候中斷到相應的地方,並顯示相應記憶體處的值,當然它是需要組態的... 此類軟體有CRACKCODE2000和註冊機編寫器keymake,具體用法你可以參考軟體的聯機說明 ^_^
我們剩下的問題就是如何來找個這關鍵Call了,基本上來說你就用前邊給你講爆破時的那種方法就可以了,很簡單的
但是就像你家後門的玻璃可能永遠擦不乾淨一樣,我們家後門的玻璃也從來沒擦乾淨過 導演:NG!重說,就像所有事情都有例外一樣,有些軟體的關鍵Call會比較難找一點,但如果你掌握了適當的方法,同樣也會很好找的...
我們就來玩玩吧:
首先,我們還來用CHINAZIP這個軟體上上手^_^
它已經是我們的老朋友了,所以就不用再介紹它了吧
好的,我們先裝上它(嘿嘿,偶就是喜歡說廢話,你打偶偶也要說^_^)接著我們點說明 -註冊,輸入Name:Suunb[CCG],Code:19870219
然後請出我們的老夥伴TRW2000,下bpx hmemcpy 按F5點確定被攔:
KERNEL?HMEMCPY
0147:9e62 push bp
0147:9e63 mov bp,sp
0147:9e65 push ds
0147:9e66 push edi
0147:9e68 push esi
0147:9e6a cld
0147:9e6b mov ecx,[bp+06]
0147:9e6f jcxz 9ee9
...省略N多程式碼...
輸入bc *,移除斷點。pmodule ,直接跳到程序領空:
0167:00436d13 mov [ebx+0c],eax
0167:00436d16 mov eax,[ebx]
0167:00436d18 cmp eax,byte +0c
0167:00436d1b jnz 00436d38
0167:00436d1d mov edx,[ebx+08]
0167:00436d20 push edx
0167:00436d21 mov ecx,[ebx+04]
0167:00436d24 mov edx,eax
0167:00436d26 mov eax,esi
0167:00436d28 call 00432b24
...省略N多程式碼...
按8下F12就會提示出現錯誤,我們第二次就按7次 接著我們再來按F10,按16下就會報告錯誤,好的,我們再來:這一次我們按F10的時候,就按我前邊說過的方法,到與上次按的次數相差五六次的時候就慢下來。好的,我們按十來下的時候就慢下來仔細瞅瞅,哈哈,一下子就看到004f4dec處的那個跳轉以及它上面的關鍵CALL了 我們按F10單步執行到004f4de7處(即關鍵CALL處)後下指令d edx就可看到真正的註冊碼,而d eax則可以看到我剛才輸入的19870219 程式碼給你:
0167:004f4dc4 mov eax,[ebp-08] <---7下F12,1下F10就來到這裡(此時ebp-08處放的是剛才輸入的註冊碼19870219)
0167:004f4dc7 push eax <---將EAX壓棧;
0167:004f4dc8 lea edx,[ebp-10]
0167:004f4dcb mov eax,[ebx+02e0]
0167:004f4dd1 call 00432f24 <---該CALL用來得到用戶輸入的用戶名,其實就是某個API函數,嘿嘿,好奇的話可以追進去看看
0167:004f4dd6 mov edx,[ebp-10] <---將得到的用戶名放入EDX;
0167:004f4dd9 lea ecx,[ebp-0c]
0167:004f4ddc mov eax,ebx
0167:004f4dde call 004f4fac <---該CALL用來計算出真正的註冊碼;
0167:004f4de3 mov edx,[ebp-0c] <---將計算出的真.註冊碼放入EDX,在下條指令時可用D EDX檢視;
0167:004f4de6 pop eax <---先前壓入的註冊碼出棧;
0167:004f4de7 call 0040411c <---該CALL用來比較兩個註冊碼,罪魁禍首啊!;
0167:004f4dec jnz 004f4e64 <---不相等則跳,跳必死,暴破將75改為74或EB,當然90也行;
0167:004f4dee mov dl,01
0167:004f4df0 mov eax,[00452558]
0167:004f4df5 call 00452658
0167:004f4dfa mov [ebp-04],eax
0167:004f4dfd xor eax,eax
0167:004f4dff push ebp
0167:004f4e00 push dword 004f4e5d
0167:004f4e05 push dword [fs:eax]
0167:004f4e08 mov [fs:eax],esp
0167:004f4e0b mov cl,01
0167:004f4e0d mov edx,004f4ea8
0167:004f4e12 mov eax,[ebp-04]
0167:004f4e15 call 0045283c
0167:004f4e1a mov ecx,004f4ecc
0167:004f4e1f mov edx,004f4ef4
0167:004f4e24 mov eax,[ebp-04]
0167:004f4e27 call 00452c80
0167:004f4e2c mov eax,004f4f00
0167:004f4e31 call 00458b8c
0167:004f4e36 mov eax,[0050306c]
0167:004f4e3b mov eax,[eax]
0167:004f4e3d mov edx,004f4f24
0167:004f4e42 call 00432f54
0167:004f4e47 xor eax,eax
0167:004f4e49 pop edx
0167:004f4e4a pop ecx
0167:004f4e4b pop ecx
0167:004f4e4c mov [fs:eax],edx
0167:004f4e4f push dword 004f4e6e
0167:004f4e54 mov eax,[ebp-04]
0167:004f4e57 call 004030c4
0167:004f4e5c ret
0167:004f4e5d jmp 00403824
0167:004f4e62 jmp short 004f4e54
0167:004f4e64 mov eax,004f4f48 <---由上面的0167:004f4dec處跳來,掛!;
0167:004f4e69 call 00458b8c
0167:004f4e6e xor eax,eax
整理:
Name:Suunb[CCG]
Code:SCCG5296
可以真接在TRW2000中下斷點bpx 004f4de6,中斷後用D EDX來檢視真.註冊碼。
另附:CRACKCODE2000的CRACKCODE.INI
[Options]
CommandLine=CHINAZIP.exe
Mode=2
First_Break_Address=4f4de7
First_Break_Address_Code=E8
First_Break_Address_Code_Lenth=5
Second_Break_Address=404123
Second_Break_Address_Code_Lenth=2
Save_Code_Address=EDX
哈哈,是不是很簡單?我說過了嘛,其實並不難的
我不知道你有沒有發現,其實上面的軟體的關鍵CALL還是很好找的,相信你用W32Dasm就中以找出來,那為什麼不用呢?對於那些比較簡單的軟體,何必非請出偵錯器呢?
給你貼個用W32Dasm找關鍵CALL的:
【軟體名稱】e族百變桌面
【軟體版本】4.0
【文件大小】1316KB
【適用平台】W.x/Me/NT/2000
【軟體簡介】提供25種變換桌面的方式,讓你的桌面煥然一新。操作簡單,無需費力學習。支持多種Internet流行圖片格式。將桌布文件打包,方便儲存於、轉發。將桌布包展開,還原圖片文件。
嘿嘿,我也懶的去折騰我的魔電上網了,咱們就還用電腦報2001年合訂本配套光碟上的軟體吧 (2002年的偶沒有買)
首先裝上它(嘿嘿,你習慣了?為什麼不丟東西了? ^_^)執行一下該軟體先,該軟體自動產生了相應的機器碼,並停留在註冊項上,輸入註冊碼19870219,點確定,掛!
用fi檢查,該軟體為Delphi編譯,沒加殼。
用W32DASM開啟該執行文件,參考-串式參考,在最下邊,見到了剛才彈出的\"註冊碼不正確,請聯係作者\"。
用滑鼠雙按,發現只有一處使用,在00488E97處,接著在串式參考對話視窗中在\"註冊碼不正確,請聯係作者\"處向上找,找到\"感謝您支持大陸軟體,祝您好運\"(說的我都不好意思了)
用滑鼠雙按,仍舊只有一處使用,在00488DF7處:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00488DCD(U)
|
:00488DD9 8B45FC mov eax, dword ptr [ebp-04]
:00488DDC 8B8020040000 mov eax, dword ptr [eax+00000420]
:00488DE2 35280BB61E xor eax, 1EB60B28
:00488DE7 3B45F8 cmp eax, dword ptr [ebp-08] <---關鍵比較,? EAX來檢視軟體正確的註冊碼;
:00488DEA 0F85A0000000 jne 00488E90 <---關鍵跳轉,不相等就跳,跳必掛!
:00488DF0 6A40 push 00000040

* Possible StringData Ref from Code Obj ->\"註冊成功\"
|
:00488DF2 68D48E4800 push 00488ED4

* Possible StringData Ref from Code Obj ->\"感謝您支持大陸軟體,祝您好運!\"
|
:00488DF7 68E08E4800 push 00488EE0 <---雙按串式參考便跳到此行,我們向上找第一個跳轉處就是關鍵跳轉,關鍵跳轉上面就是關鍵比較;
:00488DFC 8B45FC mov eax, dword ptr [ebp-04]
:00488DFF E81CD2FBFF call 00446020
:00488E04 50 push eax
...省略程式碼若干...
向上看,00488DEA處有一跳轉,不相等便跳到00488E90處,跳必掛!還記的00488E97處的出現錯誤對話視窗吧! 罪魁禍首啊!
在向上一行,看00488DE7處:cmp eax, dword ptr [ebp-08],此為關鍵比較。可用? EAX檢視軟體正確的註冊碼。
整理:
開啟該軟體,在註冊碼處輸入19870219,開啟TRW2000,下斷點bpx 00488DE7,點註冊被攔。輸入? EAX得到軟體正確的註冊碼。
機器碼:533226313
註冊碼:25061473
用註冊機編寫器keymake編寫該軟體的註冊機:
點其它-另類註冊機(F8),軟體名稱輸入ePaper.exe,註冊碼選暫存器方式 EAX 十進制。
增加斷點,中斷位址:00488DE7,中斷次數:1,第一字元:3B,指令長度:3。
產生註冊機後完工,萬事OK!
嘿嘿,現在是不是覺的找軟體的註冊碼越來越像小時候玩的躲貓貓了? 可惜偶小時候沒有青梅竹馬那種檔案類型的夥伴...
好的,我們這次講個有點兒名氣的軟體,WinZIP8.1,這個軟體相信大家都用過吧,反正偶是喜歡用RAR,不過也多少用過幾天這玩意兒...
如果你沒聽說過,那看介紹好了
【軟體名稱】WinZIP
【軟體版本】8.1 Beta 2
【文件大小】1757KB
【適用平台】W.x/Me/NT/2000
【軟體簡介】一個強大並且易用的壓縮實用程序,支持ZIP、CAB、TAR、GZIP、MIME,以及更多格式的壓縮檔案。其特點是緊密地與Windows檔案總管拖放整合,不用離開檔案總管而進行壓縮、解壓縮。
不用我說了吧,出處仍舊是電腦報2001年合訂本的配套光碟
我之所以先擇它,是因為覺得它的關鍵CALL沒有前邊那兩個那樣好找(其實也就那樣了^_^)極具代表性,而且通過它可以讓你感受一下Ollydbg這個魅力比你家的荼幾還大的偵錯器
這裡之所以提到Ollydbg,是覺的它真是一個非常非常棒的偵錯器...強烈建議你多玩幾次...(MP3好聽嗎? ^_^)
我們來吧,首先當然還是要裝上它(左閃術,右閃術),然後用Ollydbg來載入,此時界面會被分成四個部分,左上方是軟體反彙編後的程式碼,右上方是暫存器開會的地方,左下方是記憶體區,右下方顯示的則是堆疊的訊息。
我們來下斷點,按Alt+F4,之後選USER32,然後再滑鼠右鍵-->搜尋-->當前模組中的名稱,然後在那一大堆函數中找到GetDlgItemTextA,按F2來下斷點,它會提示你錯誤,並說無法設定中斷點,是不是很過癮?(嗚嗚嗚...大哥,我錯了,再也不敢了...)
哈哈,這個我也不知道什麼原因,明明是用了這個函數嘛,就是不讓斷,其實我對Ollydbg也不是太那個(關鍵是討厭它的下斷方式)看來還是用我們的萬能斷點吧,輸入註冊名Suunb[CCG],輸入註冊碼19870219,然後用TRW2000下斷bpx hmemcpy,斷到之後,pmodule返回領空後一次F12就會出現錯誤,看來所有的東東就在這裡了...
我們用TRW2000再斷一下,返回領空之後記著第一條指令的位址0040bd5f,嗚嗚嗚...上條指令明明是使用GetDlgItemTextA,為什麼在Ollydbg中不讓下呢?
沒關係,我們記下這個位址後仍舊用Ollydbg來載入程序,之後在反彙編視窗中找到0040bd5f處,然後按下F2來下斷(會變為紅色),下斷之後便按F9來執行程序,接著輸入註冊名Suunb[CCG],註冊碼19870219後按確定,程序會被Ollydbg給斷到:
0040BD5F |. 57 PUSH EDI
0040BD60 |. E8 F34A0500 CALL WINZIP32.00460858
0040BD65 |. 57 PUSH EDI ; /Arg1
0040BD66 |. E8 164B0500 CALL WINZIP32.00460881 ; \\WINZIP32.00460881
0040BD6B |. 59 POP ECX
0040BD6C |. BE 1CCA4C00 MOV ESI,WINZIP32.004CCA1C
0040BD71 |. 59 POP ECX
0040BD72 |. 6A 0B PUSH 0B ; /Count = B (11.)
0040BD74 |. 56 PUSH ESI ; |Buffer => WINZIP32.004CCA1C
0040BD75 |. 68 810C0000 PUSH 0C81 ; |ControlID = C81 (3201.)
0040BD7A |. 53 PUSH EBX ; |hWnd
0040BD7B |. FF15 F4C54A00 CALL DWORD PTR DS:[<&USER32.GetDlgItemTe>; \\GetDlgItemTextA
0040BD81 |. 56 PUSH ESI
0040BD82 |. E8 D14A0500 CALL WINZIP32.00460858
0040BD87 |. 56 PUSH ESI
0040BD88 |. E8 F44A0500 CALL WINZIP32.00460881
0040BD8D |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
0040BD94 |. 59 POP ECX
0040BD95 |. 59 POP ECX
0040BD96 |. 74 5F JE SHORT WINZIP32.0040BDF7
0040BD98 |. 803D 1CCA4C00 >CMP BYTE PTR DS:[4CCA1C],0
0040BD9F |. 74 56 JE SHORT WINZIP32.0040BDF7
0040BDA1 |. E8 31F9FFFF CALL WINZIP32.0040B6D7 <--關鍵CALL,等會兒進去玩玩
0040BDA6 |. 84C0 TEST AL,AL <--根據關鍵CALL中比較的結果來做相應的測試
0040BDA8 |. 74 4D JE SHORT WINZIP32.0040BDF7 <--跳走就沒戲!
0040BDAA |. 57 PUSH EDI
0040BDAB |. 68 08DE4B00 PUSH WINZIP32.004BDE08 ; ASCII \"Name\"
0040BDB0 |. FF35 1CC74A00 PUSH DWORD PTR DS:[4AC71C] ; WINZIP32.004BDDEC
0040BDB6 |. E8 8AFA0400 CALL WINZIP32.0045B845
0040BDBB |. 56 PUSH ESI
0040BDBC |. 68 C8EB4B00 PUSH WINZIP32.004BEBC8 ; ASCII \"SN\"
0040BDC1 |. FF35 1CC74A00 PUSH DWORD PTR DS:[4AC71C] ; WINZIP32.004BDDEC
0040BDC7 |. E8 79FA0400 CALL WINZIP32.0045B845
0040BDCC |. FF35 18C74A00 PUSH DWORD PTR DS:[4AC718] ; |Arg4 = 004BDDF4 ASCII \"winzip32.ini\"
0040BDD2 |. 6A 00 PUSH 0 ; |Arg3 = 00000000
0040BDD4 |. 6A 00 PUSH 0 ; |Arg2 = 00000000
0040BDD6 |. 68 14DE4B00 PUSH WINZIP32.004BDE14 ; |Arg1 = 004BDE14 ASCII \"rrs\"
0040BDDB |. E8 4CFA0400 CALL WINZIP32.0045B82C ; \\WINZIP32.0045B82C
0040BDE0 |. A1 A8914C00 MOV EAX,DWORD PTR DS:[4C91A8]
0040BDE5 |. 83C4 28 ADD ESP,28
0040BDE8 |. 85C0 TEST EAX,EAX
0040BDEA |. 74 07 JE SHORT WINZIP32.0040BDF3
0040BDEC |. 50 PUSH EAX ; /hObject => 000013F4 (font)
0040BDED |. FF15 80C04A00 CALL DWORD PTR DS:[<&GDI32.DeleteObject>>; \\DeleteObject
0040BDF3 |> 6A 01 PUSH 1
0040BDF5 |. EB 30 JMP SHORT WINZIP32.0040BE27
0040BDF7 |> E8 C3020000 CALL WINZIP32.0040C0BF
0040BDFC |. 68 8E020000 PUSH 28E
0040BE01 |. E8 61470500 CALL WINZIP32.00460567
0040BE06 |. 50 PUSH EAX ; |Arg3
0040BE07 |. 53 PUSH EBX ; |Arg2
0040BE08 |. 6A 3D PUSH 3D ; |Arg1 = 0000003D
0040BE0A |. E8 C8050400 CALL WINZIP32.0044C3D7 ; \\WINZIP32.0044C3D7

我們用Ollydbg斷到之後,可以像在TRW2000中一樣通過F8(這個偵錯器跟我一樣,也不喜歡F4^_^)來單步執行程序,我們按32下F8後程序就會出現錯誤,那我們在第二遍載入時按F8按到20多下時就仔細看看有沒有可疑的地方,你一眼就可以看到0040BDA1處的這個關鍵CALL,我們只要追到這裡時追進去就有可能看到軟體正確的註冊碼
那還等什麼呢?我們就進去吧...
按F7跟進後你會看的眼花眼花繚亂,到處都是PUSH跟POP,到底哪個才是呢?現在知道我為什麼讓你用Ollydbg了吧(偶起初也是要用TRW2000的,但臨時改變主意 ^_^)用Ollydbg的一個最大好處就是可以真接看到暫存器中的值,特別是你通過F8來單步執行的時候,在反彙編程式碼的下邊,會有一個小表單,在那裡可以顯示相關指令中所使用的暫存器的值,爽吧!
我們按76下F8之後,在0040B803處就可以第一次看到正確的註冊碼了,哈哈,我這邊兒是71C20EDC,然後你還會再陸續看到幾次,爽?
另外我還發現一個有趣的事情,在WinZIP8.1中,一個註冊名可以有兩個註冊碼,哈哈,不知道是不是還有為特別用戶準備的特別註冊碼以用來和普通的做區別 當程序通過比較,發現你輸入的註冊碼不正確後竟然會再次算出另一個註冊碼來再比較一次,嘿嘿,我的第二個註冊碼是25170288
追入關鍵CALL裡的程式碼:
0040B6D7 /$ 55 PUSH EBP
0040B6D8 |. 8BEC MOV EBP,ESP
0040B6DA |. 81EC 0C020000 SUB ESP,20C
0040B6E0 |. 8065 FF 00 AND BYTE PTR SS:[EBP-1],0
0040B6E4 |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
0040B6EB |. 53 PUSH EBX
0040B6EC |. 56 PUSH ESI
0040B6ED |. 57 PUSH EDI
0040B6EE |. 0F84 FB000000 JE WINZIP32.0040B7EF
0040B6F4 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B6F7 |. 50 PUSH EAX
0040B6F8 |. 68 C0E84B00 PUSH WINZIP32.004BE8C0
0040B6FD |. E8 DE61FFFF CALL WINZIP32.004018E0
0040B702 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B705 |. 50 PUSH EAX
0040B706 |. E8 F57C0800 CALL WINZIP32.00493400
0040B70B |. 83C4 0C ADD ESP,0C
0040B70E |. 83F8 14 CMP EAX,14
0040B711 |. 72 11 JB SHORT WINZIP32.0040B724
0040B713 |. BF 20C74A00 MOV EDI,WINZIP32.004AC720 ; ASCII \"auth.c\"
0040B718 |. 6A 21 PUSH 21
0040B71A |. 57 PUSH EDI
0040B71B |. E8 86F60000 CALL WINZIP32.0041ADA6
0040B720 |. 59 POP ECX
0040B721 |. 59 POP ECX
0040B722 |. EB 05 JMP SHORT WINZIP32.0040B729
0040B724 |> BF 20C74A00 MOV EDI,WINZIP32.004AC720 ; ASCII \"auth.c\"
0040B729 |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B72F |. BB F0C94C00 MOV EBX,WINZIP32.004CC9F0 ; ASCII \"Suunb[CCG]\"
0040B734 |. 50 PUSH EAX
0040B735 |. 53 PUSH EBX
0040B736 |. E8 50030000 CALL WINZIP32.0040BA8B
0040B73B |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B741 |. 50 PUSH EAX
0040B742 |. E8 B97C0800 CALL WINZIP32.00493400
0040B747 |. BE C8000000 MOV ESI,0C8
0040B74C |. 83C4 0C ADD ESP,0C
0040B74F |. 3BC6 CMP EAX,ESI
0040B751 |. 72 0A JB SHORT WINZIP32.0040B75D
0040B753 |. 6A 23 PUSH 23
0040B755 |. 57 PUSH EDI
0040B756 |. E8 4BF60000 CALL WINZIP32.0041ADA6
0040B75B |. 59 POP ECX
0040B75C |. 59 POP ECX
0040B75D |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B763 |. 50 PUSH EAX
0040B764 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B767 |. 50 PUSH EAX
0040B768 |. E8 03300900 CALL WINZIP32.0049E770
0040B76D |. 59 POP ECX
0040B76E |. 85C0 TEST EAX,EAX
0040B770 |. 59 POP ECX
0040B771 |. 75 04 JNZ SHORT WINZIP32.0040B777
0040B773 |. C645 FF 01 MOV BYTE PTR SS:[EBP-1],1
0040B777 |> 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B77A |. 50 PUSH EAX
0040B77B |. 68 D0E84B00 PUSH WINZIP32.004BE8D0
0040B780 |. E8 5B61FFFF CALL WINZIP32.004018E0
0040B785 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B788 |. 50 PUSH EAX
0040B789 |. E8 727C0800 CALL WINZIP32.00493400
0040B78E |. 83C4 0C ADD ESP,0C
0040B791 |. 83F8 14 CMP EAX,14
0040B794 |. 72 0A JB SHORT WINZIP32.0040B7A0
0040B796 |. 6A 27 PUSH 27
0040B798 |. 57 PUSH EDI
0040B799 |. E8 08F60000 CALL WINZIP32.0041ADA6
0040B79E |. 59 POP ECX
0040B79F |. 59 POP ECX
0040B7A0 |> 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B7A3 |. 50 PUSH EAX
0040B7A4 |. 53 PUSH EBX
0040B7A5 |. E8 C62F0900 CALL WINZIP32.0049E770
0040B7AA |. 59 POP ECX
0040B7AB |. 85C0 TEST EAX,EAX
0040B7AD |. 59 POP ECX
0040B7AE |. 75 0E JNZ SHORT WINZIP32.0040B7BE
0040B7B0 |. FF15 F0C14A00 CALL DWORD PTR DS:[<&KERNEL32.GetTickCou>; [GetTickCount
0040B7B6 |. A8 01 TEST AL,1
0040B7B8 |. 74 04 JE SHORT WINZIP32.0040B7BE
0040B7BA |. C645 FF 01 MOV BYTE PTR SS:[EBP-1],1
0040B7BE |> 6A 14 PUSH 14
0040B7C0 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B7C3 |. 6A 00 PUSH 0
0040B7C5 |. 50 PUSH EAX
0040B7C6 |. E8 75820800 CALL WINZIP32.00493A40
0040B7CB |. 56 PUSH ESI
0040B7CC |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B7D2 |. 6A 00 PUSH 0
0040B7D4 |. 50 PUSH EAX
0040B7D5 |. E8 66820800 CALL WINZIP32.00493A40
0040B7DA |. 83C4 18 ADD ESP,18
0040B7DD |. 807D FF 00 CMP BYTE PTR SS:[EBP-1],0
0040B7E1 |. 74 13 JE SHORT WINZIP32.0040B7F6
0040B7E3 |. E8 D7080000 CALL WINZIP32.0040C0BF
0040B7E8 |. 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
0040B7EF |> 32C0 XOR AL,AL
0040B7F1 |. E9 F5000000 JMP WINZIP32.0040B8EB
0040B7F6 |> 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B7FC |. 50 PUSH EAX
0040B7FD |. 53 PUSH EBX
0040B7FE |. E8 ED000000 CALL WINZIP32.0040B8F0 <--參與計算軟正確的註冊碼
0040B803 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--在這裡第一次發現軟體正確的註冊碼
0040B809 |. 50 PUSH EAX
0040B80A |. E8 F17B0800 CALL WINZIP32.00493400
0040B80F |. BE 2C010000 MOV ESI,12C
0040B814 |. 83C4 0C ADD ESP,0C
0040B817 |. 3BC6 CMP EAX,ESI
0040B819 |. 72 0A JB SHORT WINZIP32.0040B825
0040B81B |. 6A 39 PUSH 39
0040B81D |. 57 PUSH EDI
0040B81E |. E8 83F50000 CALL WINZIP32.0041ADA6
0040B823 |. 59 POP ECX
0040B824 |. 59 POP ECX
0040B825 |> BF 1CCA4C00 MOV EDI,WINZIP32.004CCA1C ; ASCII \"19870219\" <--將剛才輸入的錯誤的註冊碼放入EDI
0040B82A |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--EAX中裝入正確的註冊碼所在的位址
0040B830 |. 57 PUSH EDI <--用戶輸入的註冊碼入棧
0040B831 |. 50 PUSH EAX <--軟體計算出的正確的註冊碼入棧
0040B832 |. E8 392F0900 CALL WINZIP32.0049E770 <--關鍵CALL,用於比較用戶輸入的註冊碼
0040B837 |. F7D8 NEG EAX
0040B839 |. 1AC0 SBB AL,AL
0040B83B |. 59 POP ECX
0040B83C |. FEC0 INC AL
0040B83E |. 59 POP ECX
0040B83F |. A2 EDBF4C00 MOV BYTE PTR DS:[4CBFED],AL
0040B844 |. 0F85 8A000000 JNZ WINZIP32.0040B8D4
0040B84A |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B850 |. 50 PUSH EAX
0040B851 |. 53 PUSH EBX
0040B852 |. E8 33010000 CALL WINZIP32.0040B98A <--參與計算軟體的第二個註冊碼
0040B857 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--此時軟體會再算出另外一個註冊碼
0040B85D |. 50 PUSH EAX
0040B85E |. E8 9D7B0800 CALL WINZIP32.00493400
0040B863 |. 83C4 0C ADD ESP,0C
0040B866 |. 3BC6 CMP EAX,ESI
0040B868 |. 72 0E JB SHORT WINZIP32.0040B878
0040B86A |. 6A 3E PUSH 3E
0040B86C |. 68 20C74A00 PUSH WINZIP32.004AC720 ; ASCII \"auth.c\"
0040B871 |. E8 30F50000 CALL WINZIP32.0041ADA6
0040B876 |. 59 POP ECX
0040B877 |. 59 POP ECX
0040B878 |> 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--軟體計算出的第二個註冊碼裝入EAX中
0040B87E |. 57 PUSH EDI <--用戶輸入的註冊碼入棧
0040B87F |. 50 PUSH EAX <--軟體計算出的第二個註冊碼入棧
0040B880 |. E8 EB2E0900 CALL WINZIP32.0049E770 <--另一個關鍵CALL,用於比較第二次產生的註冊碼
0040B885 |. F7D8 NEG EAX
0040B887 |. 1AC0 SBB AL,AL
0040B889 |. 59 POP ECX
0040B88A |. FEC0 INC AL
0040B88C |. 59 POP ECX
0040B88D |. A2 EDBF4C00 MOV BYTE PTR DS:[4CBFED],AL
0040B892 |. 75 40 JNZ SHORT WINZIP32.0040B8D4
0040B894 |. 8D85 C0FEFFFF LEA EAX,DWORD PTR SS:[EBP-140]
0040B89A |. 6A 04 PUSH 4
0040B89C |. 50 PUSH EAX
0040B89D |. 57 PUSH EDI
0040B89E |. E8 DD690900 CALL WINZIP32.004A2280
0040B8A3 |. 83C4 0C ADD ESP,0C
0040B8A6 |. 85C0 TEST EAX,EAX
0040B8A8 |. 75 23 JNZ SHORT WINZIP32.0040B8CD
0040B8AA |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B8B0 |. 6A 04 PUSH 4
0040B8B2 |. 50 PUSH EAX
0040B8B3 |. 68 20CA4C00 PUSH WINZIP32.004CCA20 ; ASCII \"0219\"
0040B8B8 |. E8 C3690900 CALL WINZIP32.004A2280
0040B8BD |. 83C4 0C ADD ESP,0C
0040B8C0 |. 85C0 TEST EAX,EAX
0040B8C2 |. 75 09 JNZ SHORT WINZIP32.0040B8CD
0040B8C4 |. C605 EDBF4C00 >MOV BYTE PTR DS:[4CBFED],1
0040B8CB |. EB 07 JMP SHORT WINZIP32.0040B8D4
0040B8CD |> 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
0040B8D4 |> 56 PUSH ESI
0040B8D5 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B8DB |. 6A 00 PUSH 0
0040B8DD |. 50 PUSH EAX
0040B8DE |. E8 5D810800 CALL WINZIP32.00493A40
0040B8E3 |. A0 EDBF4C00 MOV AL,BYTE PTR DS:[4CBFED]
0040B8E8 |. 83C4 0C ADD ESP,0C
0040B8EB |> 5F POP EDI
0040B8EC |. 5E POP ESI
0040B8ED |. 5B POP EBX
0040B8EE |. C9 LEAVE
0040B8EF \\. C3 RETN

整理一下:
註冊名:Suunb[CCG]
註冊碼:71C20EDC or 25170288
其實如果你坐在那裡肯花上一杯茶的功夫來仔細想一下,就會知道,其實一點兒也不難,只是有一點點麻煩而以
這一章也就到這裡吧,我現在巨困無比...
最後說一下的是,現在有仍有N多的軟體用的是明碼的比較方法,所以,要想找一兩個軟體練練手還是挺容易的
這一章本來還打算講一下那些非明碼比較的軟體的,但忽然發現,如果通過非明碼比較的軟體能找到註冊碼的話,那應該也就把它的算法給搞的差不多了,所以,到下一章,分析軟體的註冊算法時再講吧...

註冊碼是怎樣煉成的

第八章--註冊碼是怎樣煉成的
你應該明白的是,並不是所有的軟體作者都像你想像並希望的那笨 沒有人願意自己的軟體被別人在偵錯器中用一條d指令就能找到正確的註冊碼...要是那樣的話還出來搞什麼?
前邊兒我們講的搜尋軟體註冊碼的方法是有針對性的,必須保證的是該軟體使用的是明碼比較,這樣的話,我們只需找對地方,一個d指令就成了。那既然有明碼比較這個詞,就不難猜出還有相應的非明碼比較...非明碼比較也比較容易理解,就是軟體比較兩個註冊碼的方法不同而以,並不是計算出正確的註冊碼後就與用戶輸入的進行比較,它可能會採用每計算出一位就與注碼中的相應位比較一次,一但發現與用戶輸入的不同,就提示出現錯誤等等等等...
遇到這樣的軟體,我們其實也可以找到其相應的註冊碼,但有點兒慘,要一位一位的計下來...但是如果人家不給你面子,一但計算出某位不正確就跳走的話,那你怎麼辦?所以,國民想致富,種樹是根本...NG!所以遇到這種軟體,我們就只有對其算法進行分析,並做出註冊機才是唯一的方法(如果你想寫註冊機的話)...
你要明白,就算我們能找到那些採用明碼比較的軟體的註冊碼,原因也僅僅是因為其採用的是明碼比較,所以我們沒有什麼值的高興的地方,我們真正要做的,並不是找到一個註冊碼而以...當然如果你剛入門,那對你的提高還是很有說明 的。我們Crack一個軟體的最終目的,是對其進行相應的分析,搞懂它的註冊算法並寫出註冊機,這樣才算是成功的Crack了一個軟體,成功後的心情是難以表達的!就像你便秘了多天後一下子排了出來一樣 ^_^,哈哈這個比喻雖然粗俗,但是你可以想像一下,對一個軟體進行仔細的分析,最後一下把它的算法給搞明白了,那種感覺...我深信不疑的認為有一天你也能體會的到,偶等你
相信你以前看過那些高人大蝦的關於軟體註冊算法分析的文章,同時也相信你有過試圖跟蹤分析某軟體的舉動,雖然後來的結果另人不太滿意
其實分析一個軟體的註冊算法,這其中包括了一些技巧性方面的東西以及必要的經驗,很難想像一個連偵錯器的使用都還沒掌握的人試圖去分析一個軟體會是怎樣一個場面...嘿嘿,偶是見過的 使用偵錯器並不難,但那並不意味著你就能去分析一個軟體了,見CALL就追這樣的舉動可不是偶一個人有過的經歷,本章我盡量給你說明適當的分析方法。
相信大家都有不在父母陪同下獨自使用偵錯器的能力以及看懂大部分彙編指令的能力了吧,那就夠了!我們開始...
正式開始今天的正題,我來舉兩個例子,相信這兩個例子都有足夠的表達能力,最起碼比我們家樓下那個賣油條的表達能力要強多了...
好的,首先,我們還是請出我們的那位老朋友吧 嘿嘿,在此,偶向CHINAZIP(中華壓縮)v7.0的作者表示我內心最真誠的歉意!相信我用這個老版本的中華壓縮不會給您帶來經濟上的麻煩...
通過前邊兒兩章的講解,我們已經把這個軟體大體上給搞明白了,並且也追出了其相應的註冊碼。而我們今天的目的是對其註冊算法進行分析,並寫出註冊機!這個軟體的註冊算法其實也比較簡(並且存在Bug)用它來當例子,很能說明情況...
好的,我們開始,前邊兒追註冊碼的時候我們就已經知道了其用於計算正確註冊碼的關鍵CALL的所在位置為004f4dde,我們用TRW2000來對其進行分析!(鑒於目前大部分教學中仍以TRW2000為主,而且這個是大多數菜鳥都會用的偵錯器,偶就用這個偵錯器來做具體講解)
先啟動CHINAZIP,說明 --註冊(所以我才說這個軟體非常適合寫教學用嘛,註冊後仍然中以再次註冊)輸入註冊名Suunb[CCG],註冊碼19870219。之看按Ctrl+N呼出TRW2000,下斷點bpx 004f4dde,F5返回。
接著就按確定吧,哈哈,被TRW2000攔到了。通過前邊兩章的分析,我們以經知道了004f4dde處的這個CALL用於計算正確的註冊碼,所以我們直接按F8跟進吧!註冊碼的算法,就包涵在這個CALL中,把它給分析透了,我們也就能弄明白軟體的註冊碼是怎樣產生的了。但是要怎麼分析呢?這是一個比較嚴肅的問題,面對那一堆堆的指令,我不知道你是怎麼想的,反正我第一次時是覺的找不著北,我怎麼哪些重要哪些不重要呢?再說了,裡面又包涵了那麼多CALL,我還要一個一個地追進去看看?
哈哈,這就是我說的技巧所在了。其實也沒什麼可怕的,只要你彙編不是問題,就行了。我們首先可以先把這個計算註冊碼的CALL從頭到尾執行一遍,搞明白其中大概的跳轉以及其中某些CALL的作用,hehe~~你可以執行過一個CALL後就看一下各個暫存器的變化情況(如果暫存器中的值改變了,顏色就會變)如果某暫存器的值在CALL過之後改變了,我們就可以看一下其包含的值是何檔案類型,如是記憶體位址就用d指令看一下,如是數值就看一下是不是得到你輸入註冊名或註冊碼的位數等等,這樣的話就可以淘汰下來一大部分的CALL,因為有許多CALL的作用只是把註冊名或註冊碼裝入到記憶體中的某個位址或者得到註冊名(註冊碼)的位數或註冊碼某一位的ASCII碼,對與這些,我們不必深究。還是推薦你用Ollydbg,執行過一條指令後很多訊息都可以看到 好的,我接著說,按F8追入CALL之後先大概走一遍...我指出追入後的反彙編程式碼,並指出註釋,相應的分析看後面...
0167:004f4fac push ebp <--F8跟入後的第一條指令
0167:004f4fad mov ebp,esp
0167:004f4faf push byte +00
0167:004f4fb1 push byte +00
0167:004f4fb3 push byte +00
0167:004f4fb5 push byte +00
0167:004f4fb7 push byte +00
0167:004f4fb9 push byte +00
0167:004f4fbb push byte +00
0167:004f4fbd push ebx
0167:004f4fbe push esi
0167:004f4fbf push edi
0167:004f4fc0 mov [ebp-08],ecx
0167:004f4fc3 mov [ebp-04],edx
0167:004f4fc6 mov eax,[ebp-04]
0167:004f4fc9 call 004041c0
0167:004f4fce xor eax,eax
0167:004f4fd0 push ebp
0167:004f4fd1 push dword 004f5097
0167:004f4fd6 push dword [fs:eax]
0167:004f4fd9 mov [fs:eax],esp
0167:004f4fdc xor esi,esi
0167:004f4fde lea eax,[ebp-0c]
0167:004f4fe1 mov edx,[ebp-04]
0167:004f4fe4 call 00403e24 <--此CALL過後用於得到用戶輸入的註冊名
0167:004f4fe9 mov eax,[ebp-0c] <--將得到的註冊名的位址裝用eax暫存器
0167:004f4fec call 0040400c <--此CALL用於得到用戶輸入的註冊名的位數,並將其放入eax中
0167:004f4ff1 mov edi,eax <--將註冊名的位數裝入edi中
0167:004f4ff3 test edi,edi <--對edi進行測試
0167:004f4ff5 jng 004f5051 <--如果edi中的值為0就會跳走
0167:004f4ff7 mov ebx,01 <--ebx置1,用於後面的運算
0167:004f4ffc mov eax,[ebp-0c] <--ebp-0c中裝的是註冊名的記憶體位址,此時將其付於eax
0167:004f4fff mov al,[eax+ebx-01] <--eax中此時裝的是註冊名的記憶體位址,加上ebx中的值再減去01,用於得到註冊碼中的相應位的字元,比如說我們第一次執行到這裡的時候ebx中裝入的是01,再減去01後得到的值其實還是eax本身,這樣就能得到註冊名中的第一個字元了,而執行到後邊再跳回來時ebx會加上1,所以就能得到下一個字元了...
0167:004f5003 call 004f4f60 <--這個CALL很重要,後面會說明我們是怎樣知道它很重要的
0167:004f5008 test al,al <--在這裡我們會發現一個測試運算,對象是al,而al在前邊CALL之前剛裝入了註冊名中的某一個字元,所以我們可以斷定上面的那個CALL會對得到的字元做上一些手腳,待會兒我們再跟入...
0167:004f500a jz 004f5031 <--如果al中裝的是0就跳到004f5031處,而al中的值會被004f5003處的那個CALL所改變
0167:004f500c lea eax,[ebp-18]
0167:004f500f mov edx,[ebp-0c] <--ebp-0c中裝的是註冊名的記憶體位址,此時裝入edx中
0167:004f5012 mov dl,[edx+ebx-01] <--跟前邊兒004f4fff處的指令道理相同,得到註冊碼中的當前參加運算的字元
0167:004f5016 call 00403f34 <--不重要!!
0167:004f501b mov eax,[ebp-18]
0167:004f501e lea edx,[ebp-14]
0167:004f5021 call 004088ac <--不重要!!
0167:004f5026 mov edx,[ebp-14]
0167:004f5029 lea eax,[ebp-10]
0167:004f502c call 00404014 <--該CALL同樣比較重要,其作用是這樣的,如果當前參加運算的字元在前邊004f5003的CALL裡進行運算之後符合了要求(符合要求後al會被置非0值)那麼在004f500a處的跳轉將會失去作用,而執行到這裡後該CALL會將現用的這個符合要求的字元儲存到00D3B3C4處(記憶體)!!後邊兒會再詳細說明
0167:004f5031 cmp ebx,byte +01 <--用此時ebx中裝的值減去1
0167:004f5034 jz 004f5040 <--如果為零,也就是說此時計算的是註冊名中的第一個字元的話就跳到004f5040處
0167:004f5036 mov eax,[ebp-0c] <--ebp-0c中裝的是註冊名的記憶體位址,該指令將註冊名的記憶體位址裝入eax中
0167:004f5039 movzx eax,byte [eax+ebx-02] <--用於得到上一個參加運算的字元
0167:004f503e jmp short 004f5046 <--無條件跳轉到004f5046處
0167:004f5040 mov eax,[ebp-0c] <--ebp-0c中裝的是註冊名的記憶體位址
0167:004f5043 movzx eax,byte [eax] <--得到註冊名的第一個字元
0167:004f5046 lea esi,[esi+eax*4+a8] <--!!!這一條指令就是關鍵所在,後面會說明的!!!此指令先得到本輪參加運算的字元的ASCII碼,然後乘以6,之後再加上a8(即十進制數168,哈哈,可以理解)同時再將這個字元計算得到的值與前面已經運算過的字元的值的和相加!
0167:004f504d inc ebx <--ebx加1,用於得到註冊碼的下一個字元
0167:004f504e dec edi <--edi減1,edi中裝的是註冊碼的位數
0167:004f504f jnz 004f4ffc <--不為零就跳到004f4ffc處開始對下一個字元進行運算...也就是說每計算完一個字元就將edi減去1,直到其為0也就是所有的字元全參加過運算為止。
0167:004f5051 lea edx,[ebp-1c] <--把裝註冊碼後半部分的位址裝入edx,傳給下面的CALL
0167:004f5054 mov eax,esi <--將前面計算的註冊碼的後半部分的值裝入eax中
0167:004f5056 call 00408c70 <--將前面計算得到的註冊碼後半部分的值轉換為十進制,並裝入ebp-1c中
0167:004f505b mov ecx,[ebp-1c] <--epb-1c中裝的是註冊碼的後半部分
0167:004f505e lea eax,[ebp-0c]
0167:004f5061 mov edx,[ebp-10] <--ebp-10中裝的是註冊碼的前半部分
0167:004f5064 call 00404058 <--該CALL用於將前後兩部分註冊碼合併置一起,合併後的註冊碼會存放置ebp-0c處
0167:004f5069 mov eax,[ebp-08]
0167:004f506c mov edx,[ebp-0c]
0167:004f506f call 00403de0
0167:004f5074 xor eax,eax
0167:004f5076 pop edx
0167:004f5077 pop ecx
0167:004f5078 pop ecx
0167:004f5079 mov [fs:eax],edx
0167:004f507c push dword 004f509e
0167:004f5081 lea eax,[ebp-1c]
0167:004f5084 mov edx,05
0167:004f5089 call 00403db0
0167:004f508e lea eax,[ebp-04]
0167:004f5091 call 00403d8c

哈哈,看了我加了註釋後的程式碼是不是好理解多了?你也許會問,你怎麼知道那些CALL是做什麼的?我前邊兒不是說過方法了嗎?我們先大概地過上一遍,看一下各個跳轉,然後再大大概的看一下各個CALL的作用...你以為上面這些註釋是我過一遍之後就能寫出來的?你多過幾遍,心中就會有了個大概...
你現在在想些什麼?眾人:站著說話不腰痛...我暈~~
哈哈,我盡量說的仔細一些:
其實很好理解的,我們追了進來,之後大概的看一下那些個CALL,其中一些稍有經驗的一看就知道是用來得到註冊名或長度什麼的...之後我們再從頭跟一遍...跟到004f4ff3處,會發現其會對註冊名的位數進行一個比較,看用戶是否輸入了註冊名...(也就是說如果edi中裝的註冊名的位數不大於0,即沒輸入就跳走,一會兒我會在後面說一下關於這點兒的Bug)而後在004f4ffc處我們會發現軟體會得到註冊名的記憶體位址,接下來的一條指令一看就知道是用來得到註冊名中的各個字元的,嘿嘿,見到這類指令,馬上就向下看吧,找一下下邊兒哪條指令會再跳回到004f4ffc處...哈哈,我們會在004f504f處發現目標,好了,現在我們就知道了從004f4ffc到004f504f之間的那些個指令會對註冊名中的每一個字元進行計算...
哈哈,也就是說軟體從004f4ffc處開始先是得到註冊名中的第N位字元,然後進行一系列的運算,之後執行到了004f504e處時把先前先到的註冊名的位數減去1然後看其是否為0,不為0就再跳到004f4ffc處,然後得以註冊名的N+1位再來進行計算。此舉的目的就是為了看註冊名的各位是否都被計算過了,如果不為0就說明還沒有計算完,哈哈,很簡單的道理嘛,edi中裝的是註冊名的位數,第計算過一位後就將其減1,減完了,註冊名的各位也就都參加了運算...
好的,我們再來看具體的算法部分:
在004f4ff5的跳轉,如果你輸入了註冊名,其就不會跳走...偶輸入的是Suunb[CCG],好的,此時會繼續執行到004f4ff7處,該指令對ebx進行啟始化...給它付1,然後在004f4ffc處時會將ebp-0c中裝的註冊名的記憶體位址裝入eax中,接著的004f4fff處用於得到註冊名的第一個字元,並將其裝入al。想像一下,eax中裝的是註冊名的記憶體位址,從該位址開始連續10個記憶體單元是我們輸入的註冊名S u u n b [ C C G ] 哈哈,明白了嗎?eax中裝的記憶體位址就是註冊名在記憶體中的首位址,第一次執行到這裡時ebx中裝的是1,eax+ebx-01後得到的還是註冊名的首位址,也就是S。而等到後面004f504f處的跳轉指令跳轉回來之前,會在004f504d處有一條inc指令會給ebx加1,這樣的話再執行到這裡時就會得到註冊名中的第2個字元u了,嘿嘿,第三次來之前會再給ebx加上1,明白了嗎?總知你可以把ebx中的值理解為當前參加運算的字元在註冊名中的位數,即ebx是1就是得到註冊名的第一位(S),如果ebx是2就是得到註冊名的第2位(u).
而後緊接著在004f5003處會有一個CALL等著我們,哈哈,這個CALL比較關鍵,註冊碼的一部份由它來決定,要發現它的重要性並不難,因為在004f5003處下面會有一個跳轉,跳轉之前會對al進行測試,嘿嘿,而al在CALL之前裝入的是當前參與運算的字元...並且你用偵錯器過一下這個CALL就會發現其對al進行了修改,哈哈,這個CALL會對al做一些處理,而處理的結果直接影響了後面部分的流程,所以,對於它,我們一定要跟進...最好能派出兩個人在邊路對其進行防守,並找專門的後位對其盯梢...

我們待會兒再跟進它,現在還是要先搞明白軟體大體上的算法。好的,我接著說,在004f5008處對al進行了測試之後會有一個跳轉,即如果al中此時裝的值為0就跳到004f5031處去...你可以理解為這個CALL會對字元進行一些運算,如果符合了要求,al就會被置0或1什麼的,出來後的測試用來判斷當前字元是否符合要求,如果符合就跳或不符合就跳...
繼續,由於我輸入的註冊名的第一個字元是S,而S剛好能通過004f5003處的那個CALL的計算 所以就沒有跳走,我繼續按F10進行單步執行...接下來的004f500c、004f500f、004f5012這三條指令跟前邊兒的得到註冊碼第N位字元的指令道理是一樣的,你看註釋好了...而後面從004f5016到004f5029處的這幾條指令也沒什麼好講的,對中間的兩個CALL好奇的話可以進去大概看一下。得不到什麼實質性的東西...而004f502c處的這個CALL嘛,就很重要了,哈哈,它的作用是什麼呢?還記的我剛才說過的004f5003處的那個CALL吧,它執行過後會使al發生變化,它下面的跳轉指令會根據al的值做相應跳轉,即如果al為0,就跳到004f5031處,剛好就跳過了004f502c處的這個CALL...而我輸入的第一個字元是S,剛好符合了004f5003處那個CALL的要求,所以沒有跳走,於是就執行到了這裡,你可以追進去看一下,裡面並不複雜,只是將當前參加運算的字元裝入記憶體的00D3B3C4處(如果當前參加運算的字元在004f5003處沒有通過,就不會執行到這裡,哈哈,明白過來了吧,這個CALL用於收集註冊名中所有符合004f5003處那個CALL要求的字元)
HOHOHO~~(請模仿周星星式的笑聲...)現在我們已經明白了一半了...好的,我們繼續...
不管你是從004f500a處跳到004f5031處的,還是一步步執行到這裡的,總知,不管你輸入的註冊名中參加當前運算的那一個字元符不符合004f5003處的那個CALL的要求,總知都會執行到這裡...這條指令用來幹什麼呢?還記的ebx中裝的是參加運算的字元在註冊名中的相應的位數嗎?cmp ebx,byte +01 就是用ebx減去1,該條指令的用途也就是看一下當前參加運算的字元是不是註冊名中的第一個字元,如果是就跳到 004f5040處,否則繼續... 我們先看004f5040處,當執行到此處時,ebp-0c中裝的其實是註冊名的記憶體位址(前邊就已經說過了)在這裡將其裝入eax中,而後面004f5043處的指令的用途就是得到註冊名的第一個字元...好了,我們再拐回來看004f5036處,如果當前參加運算的字元不是註冊名中的第一個字元,就不會跳走,而執行到這裡時同樣將ebp-0c中裝的註冊名的記憶體位址放入eax中,而004f5039處的eax,byte [eax+ebx-02]嘛,哈哈,很好理解,eax+ebx-01得到的是當前參加運算的字元的記憶體位址,而這裡的eax+ebx-02得到的就是當前參加運算的字元的前面的那個字元,瞭解?
我們接著看004f5046處的那條指令吧,這個同樣非常重要,它的作用是計算註冊碼的後半部分!
我相信你很容易就能理解它的意思了,當執行到這裡時,eax中裝的或者是註冊碼中的第一個字元,或者是當前參加運算的字元的前一個字元(注:字元在記憶體或暫存器中是以ASCII碼來表示的,如S在eax中會顯示為00000053,而S的ASCII碼便是53,十進制為83)...我們第一次執行到這裡時,esi中的值為0(即00000000)eax*4+a8的意思就是用當前參加運算的字元的ASCII碼乘以4,再用積加上a8(也就是十進制數168,一路發?)再用這個和與esi相加,我已經說過了,第一次執行到這裡時esi中的值為0...而當第二次執行到這裡時,esi中裝的便是註冊名的第一個字元的ASCII碼乘以4再加一路發的和...
你會問你為什麼知道它是計算註冊碼的後半部分的?猜的!!哈哈,當然不是,我們可以看到,在004f5054處,程序會將前面計算的結果裝用eax中,後邊兒緊接著就是一個CALL,嘿嘿,光天化日之下,這也太明顯了吧,我們追進去大概看一下就知道它的作用是將十六進制的數轉換為十進制的...並將轉換後的結果裝入edx中裝的記憶體位址處,在CALL之前我們會看到edx中的值以由004f5051處裝入,即ebp-1c,哈哈,CALL過之後你用d ebp-1c看一下,就會看到你註冊碼的後半部分了...
而後程序會在004f505b將註冊碼後半部分裝入ecx中,在004f505e處時會將一個記憶體位址ebp-0c裝入eax處(它的作用就是起一個傳送參數的作用,在待會兒的CALL中會用eax中裝入的值來存放結果)之後的004f5061處會將ebp-10裝入edx中,ebp-10處裝的是什麼呢?我們用d ebp-10指令看一下就會知道它的位址為00D3B3C4,嘿嘿,你的嗅覺敏感嗎?不敏感的話我就再說一遍,還記的004f502c處的那個CALL嗎?它的作用就是收集符合004f5003處的那個CALL的要求的字元...
嘿嘿,你明白過來了嗎?
這個軟體的註冊算法是這樣的:首先得到註冊碼的位數,看其是否大於0,不大於0就跳到004f5051處...好的,我們輸入了Suunb[CCG]這個註冊名,此時的註冊碼位數就是10,所以不會跳走,之後我們會來到004f4fff處,第一次執行到這裡時會將註冊名的第一個字元S裝入al中,第二次來時會將註冊名中的第二個字元(即u)裝入al中,它的作用就是將當前參加運算的字元裝入al中,之後緊接著就是一個CALL,這個CALL會對當前參加運算的字元進行計算...接著出來會有一個跳轉,看al中裝的是不是0,如果是就跳到004f5031處,如果不是非0值就說明當前這個字元符合了要求,那麼就會執行到004f502c處,這裡的CALL會將其存放置記憶體的00D3B3C4處...而後到了004f5031處會有一個比較,作用是看當前參加運算的字元是不是註冊名中的第一個字元,是的話就跳到004f5040處,在此將註冊名的第一個字元裝入eax,用來參加004f5046處的計算。如果當前參加運算的不是註冊名的第一個字元,那麼就會在執行到004f5039處時得到當前參加運算的字元前面的那個字元,將其裝入eax後就無條件跳到004f5046處來參加運算。瞭解?也就是說你輸入的註冊名的第一個字元會參加兩次計算,而最後一個字元不會參加計算(想想看,如果當前參加運算的字元是註冊名中的第一個字元,它會參加計算,如果是第二個,就取前邊的一個,即第一個又會參加一次計算,到了第三個的時候取第二個,到了第四個的時候取第三個...而當最後一個字元來到這裡時會取前邊的那個字元來參加運算,而這之後就循環就結束了,所以,最後一個不會被計算入內)等到註冊名中的所有字元都參加過了運算,就會來到004f5056處,在這裡將前面004f5046處的計算結果轉換為十進制...而後會在後面的004f5064處的那個CALL裡,將其與先前裝入00D3B3C4處的所有符合004f5003處的CALL要求的字元合併到一起,這個結果,嘿嘿,就是真正的註冊碼了!
也就是說,真正的註冊碼是由以下部分組成的:
你輸入的註冊名中的所有符合004f5003處的那個CALL要求的字元+((註冊名中的第一個字元的ASCII碼*4+168)*2+(除第一位和最後一位外的所有字元)*4+168的和的和)
現在我們也知道註冊碼是怎樣煉成了的 那麼我們要寫註冊機,就一定要把004f5003處的那個CALL給搞明白,這樣的話,我們才能對註冊名中的字元進行篩選...
好的,我們再來:
Ctrl+N呼出TRW2000,下bpx 004f5003,F5結束點確定被攔後便按F8跟進,哈哈,這個就沒什麼好說的了,看我指出的註釋吧:
0167:004f4f60 push ebp
0167:004f4f61 mov ebp,esp
0167:004f4f63 push ecx
0167:004f4f64 push ebx
0167:004f4f65 push esi
0167:004f4f66 mov [ebp-01],al <--將字元裝入記憶體ebp-01處
0167:004f4f69 mov byte [ebp-03],02 <--ebp-03處裝入02
0167:004f4f6d mov byte [ebp-02],01 <--ebp-02處裝入01
0167:004f4f71 mov cl,[ebp-01] <--將參加運算的字元裝入cl
0167:004f4f74 dec ecx <--cl減1
0167:004f4f75 sub cl,02 <--cl再減去2
0167:004f4f78 jc 004f4fa4 <--有進位就跳轉,你不用擔心,一般都不會跳走的啦
0167:004f4f7a inc ecx <--ecx加1,也就是cl加1
0167:004f4f7b mov bl,02 <--bl裝入02
0167:004f4f7d xor eax,eax <--eax做異或運算,即將eax置0
0167:004f4f7f mov al,[ebp-01] <--al裝入參加運算的字元
0167:004f4f82 xor edx,edx <--edx置0
0167:004f4f84 mov dl,bl <--將bl中的值付給dl
0167:004f4f86 mov esi,edx <--再付給esi
0167:004f4f88 xor edx,edx <--edx置0
0167:004f4f8a div esi <--用eax中裝的參加運算的字元的ASCII碼除以當前esi的值
0167:004f4f8c test edx,edx <--測試edx,edx中裝的是上剛才除法計算後的餘數
0167:004f4f8e jnz 004f4f93 <--不為0就跳到004f4f93處
0167:004f4f90 inc byte [ebp-03] <--ebp-03處的值加1
0167:004f4f93 cmp byte [ebp-03],02 <--用ebp-03處的值減去02
0167:004f4f97 jna 004f4f9f <--不大於就跳到004f4f9f處
0167:004f4f99 mov byte [ebp-02],00 <--ebp-02裝入00
0167:004f4f9d jmp short 004f4fa4 <--無條件跳轉到004f4fa4處
0167:004f4f9f inc ebx <--ebx加1
0167:004f4fa0 dec cl <--cl減1
0167:004f4fa2 jnz 004f4f7d <--不為0就跳到004f4f7d處再來一遍
0167:004f4fa4 mov al,[ebp-02] <--將ebp-02處的值裝入al後返回
0167:004f4fa7 pop esi
0167:004f4fa8 pop ebx
0167:004f4fa9 pop ecx
0167:004f4faa pop ebp

不知道你看不看的明白,我大概給你說明一下,大體上就是這樣的:
先得到這個字元的ASCII碼,然後用其減去2,並將bl置值02...然後用eax中裝的減了2的ASCII碼除以esi中裝的bl中的值,之後就看edx中裝的餘數是否為0,如果為0就將ebp-03處的值加1,你應該知道ebp-03處在啟始化的時候被付值為2,如果其被加了1,那就大於了2,這樣的話後面在004f4f97處就不會跳走,如果不跳走,在004f4f99處,ebp-02就會被裝入00,之後就會無條件跳轉到004f4fa4處,在那裡就會把ebp-02的值,也就是00裝入al,明白過來了吧...但如果edx中裝的餘數不為0,那麼在004f4f8e處就會跳走,到了004f4f93處時由於ebp-03中裝的是02,所以條件就會成立,從而可以跳到004f4f9f處去繼續執行程序...而到了004f4f9f處後ebx(也就是bl)會被加上1,而cl在後面會被減去1,如果cl不為0的話就再跳到004f4f7d處,再來一遍,直到cl變為零為止...
上面這個程序在Delphi中可以這樣表示:
(變數S中裝的是當前參加運算的字元,Code變數用來收集符合要求的字元,變數的聲明我沒有寫明)
N:=Ord(S);
for i:=2 to N-1 do
begin
modz:=(N-i) mod i;
if modz=0 then Break;
end;
if modz<>0 then
Code:=Code+S;
哈哈,這就起到了與前面彙編程式碼相同的作用了
下面我給你寫一個函數,該函數可以用來得到註冊碼中的所有符合要求的字元:
function GetKeyChar(Name: String): String;
var
i,ASC,sh,modz:integer;
Code:String;
begin
for i:=1 to Length(Name) do
begin
ASC:=Ord(Name);
for sh:=2 to ASC-1 do
begin
modz:=(ASC-sh) mod sh;
if modz=0 then Break;
end;
if modz<>0 then
Code:=Code+Name;
end;
Result:=Code;
end;
你可以這樣來用:
var
S1,S2:String;
begin
S1:=Edit1.text; //Edit1用來輸入註冊名;
S2:=GetKeyChar(S1); //此時S2中得到的便是註冊名中所有符合要求的字元了;
end;
嘿嘿,你現在是不是很想知道都有哪些字元能符合要求?嘿嘿,其實CHINAZIP自己就可以告訴我們,你只要在輸入註冊名的時候把所有的可用字元全填上,然後在用d指令看一下註冊碼的前半部(即非數位部分),就可以知道了,當然你也可以自己寫一個程序來試試,用Delphi的話可以直接用我上面給的函數...告訴你好了,在所有的ASCII字元中,只有以下幾個符合要求:
CGIOSYaegkmq5=%)+;/
不信的話你可以試試,正確的註冊碼中的字元只能是這幾個...嘿嘿,看來還是比較尊重我們CCG的嘛
好了,我又廢話了這麼多,把完整的註冊機給你貼出來吧,這並不難,只要加上後半部分註冊碼的計算就OK了:
你可以在Delphi中聲明以下函數,便可直接使用:
function GetKey(Name: String): String;
var
N:String;
i,sh,ci:integer;
Si:integer;
ASC,modz:integer;
begin
i:=Length(Name);
if i=0 then Result:=\'請輸入註冊名...\'
else
begin
for sh:=1 to i do
begin
ASC:=Ord(Name[sh]);
for ci:=2 to ASC-1 do
begin
modz:=(ASC-ci) mod ci;
if modz=0 then Break;
end;
if modz<>0 then N:=N+Name[sh]
end;
Si:=Ord(Name[1])*4+168;
for sh:=1 to i-1 do
begin
Si:=Si+Ord(Name[sh])*4+168;
end;
N:=UpperCase(N+inttostr(Si));
Result:=N;
end;
end;
最後順便說一下,如果你稍微留意一下,就會發現,在剛開始的004f4ff3處會對註冊名的位數進行測試,即如果位數為0就會在下一條指令時跳至004f5051處,如果你稍微有一點兒經驗,就會發覺,通常如果程序發現註冊名的位數為0,就會直接跳到失敗處,而這個軟體卻沒有!004f5051處是什麼呢?暈~~具然是要得到註冊碼的後半部分,哈哈,我們以到00D3B3C4處看看,會看到一堆0,你現在在想什麼呢?既然我們沒有輸入註冊名,那麼就不可能有註冊碼的前半部分,而後邊便會有CALL來合併前後兩部分的註冊碼 你重新啟動一下軟體,註冊名不填,把註冊碼填為0註冊一下看看...HOHO~~(請仍舊模仿周星星式的笑聲)這個粗心的作者啊,造成這樣的原因很簡單,軟體根本就沒有判斷注名是否為空並在軟體啟始化的時候把用於計算註冊碼後半部分的integer變數付了初始值0,否則的話00D3B3C4處的記憶體應該為空值..(難不成到時連0都不用輸就能註冊?)
所以說,Crack並不是一件壞事,像這種情況你完全可以告訴作者的嘛,到時不但交了一個朋友而且說不准還會得到個免費的註冊碼....(不知道有沒有白帽子Cracker?嘿嘿,CCC剛好也可以是註冊碼的前半部分哦~~)我希望你明白,對於這種註冊算法簡單且存在Bug的軟體(通常也說明其作者還沒什麼經驗 ^_^),我們不應該為能提供它註冊機而感到高興,如果能說明 其作者改善算法或去掉Bug,又何嘗不是一件好事呢?畢竟軟體上面加的有中華兩個字,你忍心???

我不知道上面給你講的中華壓縮註冊分析你是否看懂了,我個人認為我講的還是比較詳細的了(幾乎每條指令都加了註釋且又再三在後面說明)但如果你仍然看不懂的話,請務必相信是本人寫的文章不好,不要放棄啊哥們兒~~!
好了,我再來給你舉另外一個例子...通過它來給你講一下另外一種比較一般的註冊碼計算方法,即將運算的結果與一個表中的字元進行轉換,也就是常說的密碼表啦^_^
本來是想用網際快車FlashGet的,可是在看雪已經有人貼了最新的1.40版的破文&註冊機,正好前些天的時候網友啥也不是在後面隨後貼帖說要幫他看一下語音界面2.0這個軟體,down下來後大概看了一下,哈哈,發現這個正是我想要的,註冊碼計算的程序中採用了密碼表並且也不難...hehe~~後來HMILY老哥看到了啥也不是的另一個帖子,也寫個註冊機和破文,你可以參考一下,嘿嘿,HMILY跟偶是自己人,所以偶不怕他...
我們開始吧...
首先運算一下這個軟體,其會自動產生機器碼,在我這邊兒是xn2urkeUMwpNv5xZ。
這一章,我會用偵錯器Ollydbg來作講解,它真的很好用...我們開始吧:
偶不知道你是否喜歡Ollydbg的下斷方式,總知偶是不喜歡,從那麼多API裡面先(字好小),再說了,偶還是喜歡用Hmemcpy來斷,除非斷不到或在2K/XP下,否則偶才不要去跑API呢,往往要三四次才斷到,多累啊 我們還是先請臨時演員TRW2000出出一下場吧(把你的MP3先暫停一下 ),下bpx hmemcpy點確定後會被攔,pmodule後返回到0040432f處,我們就從這裡開始吧 導演:\"Stop!換吳孟達出場\" 嘿嘿,繼續你的MP3,請同時開啟Ollydbg
我們用Ollydbg載入,在反彙編程式碼處(即左上方那個子視窗中)按Ctrl+G,輸入0040432f,Enter鍵後便來到了這裡。我們大概看看,有經驗的話很容易就會看出此地便是了(這次運氣比較好,一下就能找到軟體計算註冊碼的地方 ^_^)
好吧,我們按F2在這裡下斷,接著按F9來執行程序,在註冊處輸入CHINA Cracking Group Suunb後按確定會被Ollydbg斷下來,大概跑跑看看吧 我貼出反彙編程式碼:
0040432F |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404333 |. 6A 05 PUSH 5
00404335 |. 51 PUSH ECX
00404336 |. 68 E8030000 PUSH 3E8
0040433B |. 8BCE MOV ECX,ESI
0040433D |. E8 10050000 CALL
00404342 |. 8BC8 MOV ECX,EAX
00404344 |. E8 7D060000 CALL
00404349 |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040434D |. 6A 05 PUSH 5
0040434F |. 52 PUSH EDX
00404350 |. 68 E9030000 PUSH 3E9
00404355 |. 8BCE MOV ECX,ESI
00404357 |. E8 F6040000 CALL
0040435C |. 8BC8 MOV ECX,EAX
0040435E |. E8 63060000 CALL
00404363 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
00404367 |. 6A 05 PUSH 5
00404369 |. 50 PUSH EAX
0040436A |. 68 EA030000 PUSH 3EA
0040436F |. 8BCE MOV ECX,ESI
00404371 |. E8 DC040000 CALL
00404376 |. 8BC8 MOV ECX,EAX
00404378 |. E8 49060000 CALL
0040437D |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
00404381 |. 6A 05 PUSH 5
00404383 |. 51 PUSH ECX
00404384 |. 68 EB030000 PUSH 3EB
00404389 |. 8BCE MOV ECX,ESI
0040438B |. E8 C2040000 CALL
00404390 |. 8BC8 MOV ECX,EAX
00404392 |. E8 2F060000 CALL
00404397 |. 8B7C24 68 MOV EDI,DWORD PTR SS:[ESP+68]
0040439B |. 33DB XOR EBX,EBX
0040439D |. 33C9 XOR ECX,ECX
0040439F |. 8D04BF LEA EAX,DWORD PTR DS:[EDI+EDI*4]
004043A2 |. 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4]
004043A5 |. 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] <--上面的這些你都不需要管,因為它們幫不了我們什麼忙


004043A8 |. C1E6 02 SHL ESI,2 <--執行後ESI的值為5DC,及十進制數1500
004043AB |> 0FBE440C 50 /MOVSX EAX,BYTE PTR SS:[ESP+ECX+50] <--ESP+ECX+50就是機器碼在記憶體中的位址(首次執行到這裡明ECX為0,得到的就是機器碼的第一位,第二次到這裡時ECX會加上1,得到的是第二位...)
004043B0 |. 03C6 |ADD EAX,ESI <--與ESI相加,也就是加上1500
004043B2 |. BD 3E000000 |MOV EBP,3E <--EBP置3E,及十進制數62
004043B7 |. 99 |CDQ <--增強...
004043B8 |. F7FD |IDIV EBP <--EAX中裝的機器碼的第1 or 2 or 3 or 4位與1500的和與62相除
004043BA |. 0FBE440C 54 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+54] <--ESP+ECX+54得到機器碼的第5位(還記的ESP+ECX+50中裝的是第一位嗎?首次執行到這裡明ECX為0,得到的就是機器碼的第5位,第二次到這裡時ECX會加上1,得到的是第6位...)
004043BF |. 03C6 |ADD EAX,ESI <--同樣加上1500
004043C1 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--重要的地方來了!此時EDX中裝的是前面的第1 or 2 or 3 or 4位機器碼加上1500後再除以62的餘數,那4070E4處是什麼呢?如果在TRW2000中,我們用d 004070E4就可以看到,在Ollydbg中,我們可以在左下角處按Ctrl+G來輸入相應的記憶體位址,這樣的話就可以看到了。我們會發現從4070E4開始,裝的是一串字元,依次是0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ。哈哈明白什麼意思了嗎?4070E4處指的是0,那4070E4加上edx中裝的餘數後的記憶體位址中裝的便是當前機器碼所對應的註冊碼。(如餘數為5,那麼從4070E4處的0開始往後數第灌水限制符就是了)該條指令執行過後就會把相應的註冊碼裝入dl中
004043C7 |. 88540C 30 |MOV BYTE PTR SS:[ESP+ECX+30],DL <--將機器碼的第1 or 2 or 3 or 4所對應的註冊碼裝入ESP+ECX+30處
004043CB |. 99 |CDQ <--前邊兒得到的第5 or 6 or 7 or 8位機器碼增強
004043CC |. F7FD |IDIV EBP <--同樣除以62
004043CE |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--與004043C1處作用相同,不用我多說了吧,就是得到當前機器碼的對應註冊碼
004043D4 |. 88440C 38 |MOV BYTE PTR SS:[ESP+ECX+38],AL <--裝入ESP+ECX+38處
004043D8 |. 0FBE440C 58 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+58] <--得到機器碼的第9 or 10 or 11 or 12位
004043DD |. 03C6 |ADD EAX,ESI <--與1500相加
004043DF |. 99 |CDQ <--增強....
004043E0 |. F7FD |IDIV EBP <--除以62
004043E2 |. 0FBE440C 5C |MOVSX EAX,BYTE PTR SS:[ESP+ECX+5C] <--得到機器碼的第13 or 14 or 15 or 16位
004043E7 |. 03C6 |ADD EAX,ESI <--與1500相加
004043E9 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--前邊的第9 or 10 or 11 or 12位所對應的註冊碼
004043EF |. 88540C 40 |MOV BYTE PTR SS:[ESP+ECX+40],DL <--裝入ESP+ECX+40處
004043F3 |. 99 |CDQ <--增強
004043F4 |. F7FD |IDIV EBP <--除以62
004043F6 |. 41 |INC ECX <--ECX加1
004043F7 |. 83F9 04 |CMP ECX,4 <--看ECX是否為4(前邊兒是一次計算四位的嘛,第一次計算第1、5、9、13位,第二次是2、6、10、14...)
004043FA |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--得到前邊的第13 or 14 or 15 or 16位機器碼所對應的註冊碼
00404400 |. 88440C 47 |MOV BYTE PTR SS:[ESP+ECX+47],AL <--裝入ESP+ECX+47處
00404404 |.^7C A5 \\JL SHORT LIAOCACH.004043AB <--ECX小於4就從頭再來一遍(直到16位機器碼都計算完為止)
00404406 |. 8B35 AC524000 MOV ESI,DWORD PTR DS:[<&MSVCRT.atoi>] ; MSVCRT.atoi
0040440C |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404410 |. 51 PUSH ECX ; /s
00404411 |. 885C24 38 MOV BYTE PTR SS:[ESP+38],BL ; |
00404415 |. 885C24 40 MOV BYTE PTR SS:[ESP+40],BL ; |
00404419 |. 885C24 48 MOV BYTE PTR SS:[ESP+48],BL ; |
0040441D |. 885C24 50 MOV BYTE PTR SS:[ESP+50],BL ; |
00404421 |. FFD6 CALL ESI ; \\atoi
00404423 |. 83C4 04 ADD ESP,4
00404426 |. 83F8 01 CMP EAX,1
00404429 |. 75 3C JNZ SHORT LIAOCACH.00404467
0040442B |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040442F |. 52 PUSH EDX
00404430 |. FFD6 CALL ESI
00404432 |. 83C4 04 ADD ESP,4
00404435 |. 83F8 01 CMP EAX,1
00404438 |. 75 2D JNZ SHORT LIAOCACH.00404467
0040443A |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
0040443E |. 50 PUSH EAX
0040443F |. FFD6 CALL ESI
00404441 |. 83C4 04 ADD ESP,4
00404444 |. 83F8 01 CMP EAX,1
00404447 |. 75 1E JNZ SHORT LIAOCACH.00404467
00404449 |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
0040444D |. 51 PUSH ECX
0040444E |. FFD6 CALL ESI
00404450 |. 83C4 04 ADD ESP,4
00404453 |. 83F8 01 CMP EAX,1
00404456 |. 75 0F JNZ SHORT LIAOCACH.00404467
00404458 |. 5F POP EDI
00404459 |. 5E POP ESI
0040445A |. 5D POP EBP
0040445B |. B8 FEFFFFFF MOV EAX,-2
00404460 |. 5B POP EBX
00404461 |. 83C4 54 ADD ESP,54
00404464 |. C2 0400 RETN 4
00404467 |> 8D7424 30 LEA ESI,DWORD PTR SS:[ESP+30] <--正確的註冊碼的前4位的位址裝入ESI中,嘿嘿,執行到這裡時我們就可以看到正確的註冊的前4位了,在TRW2000 or SoftICE中可以用d ESP+30來檢視而在Ollydbg中什麼都不用做就可以在左上方反彙編程式碼區與左下記憶體區中間的那個小表單中看見 在TRW2000中下過D指令後按Alt+上下鍵翻幾下,就可以看到所有的註冊碼了(它們並沒有分太開嘛0063F5E0-0063F5E3是前4位,0063F5E8-0063F5EB是5-8位,0063F5F0-0063F5F3是9-12位,0063F5F8-0063F5FB是最後4位,而你輸入的註冊碼的記憶體位址:0063F5C0-0063F5C3是前4位,0063F5C8-0063F5CB是5-8位,0063F5D0-0063F5D3是9-12位,0063F5D8-0063F5DB是最後4位,機器碼存放的位址是0063F6000-0063F60F)
0040446B |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10] <--你輸入的註冊碼的前4位的位址裝入EAX中
0040446F |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的註冊碼的第1位或第3位(EAX中的值到了後邊兒會加上2,這樣再執行到這裡時得到的就是第3位了)
00404471 |. 8ACA |MOV CL,DL <--傳入cl中
00404473 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的註冊碼的第1位或第3位比較(ESI中的值會與EAX中的值一起改變)
00404475 |. 75 1C |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404477 |. 3ACB |CMP CL,BL <--CL與BL比較,BL中的值為00000000(也就是空啦),這條指令有什麼用呢?其實很簡單了,每4位註冊碼的後面都會再跟一個空值,也就是如你在記憶體中可以看到1234.那個.就是空值,明白過來了吧,等到前4位都被測試過了,cl中就會裝入.也就是00000000,到時就可以在後面跳走了


00404479 |. 74 14 |JE SHORT LIAOCACH.0040448F <--如果CL中的值為零(即4位已經全部比較過了),就跳走
0040447B |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--EAX+1後得到的會是你輸入的註冊碼的第2位或者第4位(視EAX的值而定)
0040447E |. 8ACA |MOV CL,DL <--傳入CL
00404480 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的註冊碼的第2位或第4位比較(ESI的值會與EAX一起改變)
00404483 |. 75 0E |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404485 |. 83C0 02 |ADD EAX,2 <--EAX加上2,這樣的話待會兒再跳到0040446F處時,再得到的就是你輸入的註冊碼的第3位了
00404488 |. 83C6 02 |ADD ESI,2 <--同上,ESI加上2後再跳到0040446F處重新再來一遍時就會得到正確的註冊碼的第3位
0040448B |. 3ACB |CMP CL,BL <--再次比較CL是否為空
0040448D |.^75 E0 \\JNZ SHORT LIAOCACH.0040446F <--不為空就再跳到0040446F處,來繼續比較前4位中的1、3兩位
0040448F |> 33C0 XOR EAX,EAX <--1-4位全部比較完後會跳到這裡
00404491 |. EB 05 JMP SHORT LIAOCACH.00404498
00404493 |> 1BC0 SBB EAX,EAX
00404495 |. 83D8 FF SBB EAX,-1
00404498 |> 3BC3 CMP EAX,EBX
0040449A |. 0F85 AB000000 JNZ LIAOCACH.0040454B
004044A0 |. 8D7424 38 LEA ESI,DWORD PTR SS:[ESP+38] <--與上面的大體相同嘛,將正確註冊碼的5-8位的記憶體位址裝入ESI中
004044A4 |. 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+18] <--你輸入的註冊碼的5-8位的記憶體位址裝入EAX中
004044A8 |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的註冊碼的第5 or 7位(道理我相信你一定已經明白了)
004044AA |. 8ACA |MOV CL,DL <--再裝入CL中
004044AC |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的註冊碼的第5 or 7位比較
004044AE |. 75 1C |JNZ SHORT LIAOCACH.004044CC <--不相等就跳走
004044B0 |. 3ACB |CMP CL,BL <--與BL中的00000000比較,看5-8位是否已經全部比較完畢
004044B2 |. 74 14 |JE SHORT LIAOCACH.004044C8 <--是的話就跳走
004044B4 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你輸入的註冊碼的第6 or 8位
004044B7 |. 8ACA |MOV CL,DL <--裝入CL
004044B9 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的註冊碼的第6 or 8位比較
004044BC |. 75 0E |JNZ SHORT LIAOCACH.004044CC <--不正確就跳走
004044BE |. 83C0 02 |ADD EAX,2 <--EAX加2,這樣做的目的相信你已經知道了吧


004044C1 |. 83C6 02 |ADD ESI,2 <--ESI也加上2
004044C4 |. 3ACB |CMP CL,BL <--比較CL是否為空
004044C6 |.^75 E0 \\JNZ SHORT LIAOCACH.004044A8 <--不是就跳回去再來一遍
004044C8 |> 33C0 XOR EAX,EAX <--5-8位全部比較完後全跳到這裡
004044CA |. EB 05 JMP SHORT LIAOCACH.004044D1
004044CC |> 1BC0 SBB EAX,EAX
004044CE |. 83D8 FF SBB EAX,-1
004044D1 |> 3BC3 CMP EAX,EBX
004044D3 |. 75 76 JNZ SHORT LIAOCACH.0040454B
004044D5 |. 8D7424 40 LEA ESI,DWORD PTR SS:[ESP+40] <--將正確的註冊碼的9-12位的記憶體位址裝入ESI
004044D9 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20] <--你輸入的
004044DD |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的註冊碼的第9 or 11位
004044DF |. 8ACA |MOV CL,DL <--裝入CL
004044E1 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的註冊碼的第9 or 11位比較
004044E3 |. 75 1C |JNZ SHORT LIAOCACH.00404501 <--不對便跳走
004044E5 |. 3ACB |CMP CL,BL <--看CL是否為空,即看9-12位是否全部比較完畢
004044E7 |. 74 14 |JE SHORT LIAOCACH.004044FD <--是的話跳走
004044E9 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你輸入的註冊碼的第10 or 12位
004044EC |. 8ACA |MOV CL,DL <--裝入CL
004044EE |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的註冊碼的第10 or 12位比較
004044F1 |. 75 0E |JNZ SHORT LIAOCACH.00404501 <--不相等就跳走
004044F3 |. 83C0 02 |ADD EAX,2 <--EAX加2
004044F6 |. 83C6 02 |ADD ESI,2 <--ESI加2
004044F9 |. 3ACB |CMP CL,BL <--看是否全部比較完畢
004044FB |.^75 E0 \\JNZ SHORT LIAOCACH.004044DD <--沒有就跳回去再來一遍
004044FD |> 33C0 XOR EAX,EAX <--9-12位全部比較完畢後會跳到這裡
004044FF |. EB 05 JMP SHORT LIAOCACH.00404506
00404501 |> 1BC0 SBB EAX,EAX
00404503 |. 83D8 FF SBB EAX,-1
00404506 |> 3BC3 CMP EAX,EBX
00404508 |. 75 41 JNZ SHORT LIAOCACH.0040454B
0040450A |. 8D7424 48 LEA ESI,DWORD PTR SS:[ESP+48] <--嘿嘿,都講了三遍了,你一定明白了吧,所以我把這最後一段留給你自己來看吧,從這裡到00404530處我相信你猜也猜的出來 (眾人:恥笑你!)
0040450E |. 8D4424 28 LEA EAX,DWORD PTR SS:[ESP+28]
00404512 |> 8A10 /MOV DL,BYTE PTR DS:[EAX]
00404514 |. 8ACA |MOV CL,DL
00404516 |. 3A16 |CMP DL,BYTE PTR DS:[ESI]
00404518 |. 75 1C |JNZ SHORT LIAOCACH.00404536
0040451A |. 3ACB |CMP CL,BL
0040451C |. 74 14 |JE SHORT LIAOCACH.00404532
0040451E |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1]
00404521 |. 8ACA |MOV CL,DL
00404523 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1]
00404526 |. 75 0E |JNZ SHORT LIAOCACH.00404536
00404528 |. 83C0 02 |ADD EAX,2
0040452B |. 83C6 02 |ADD ESI,2
0040452E |. 3ACB |CMP CL,BL
00404530 |.^75 E0 \\JNZ SHORT LIAOCACH.00404512
00404532 |> 33C0 XOR EAX,EAX <--全部通過後來到這裡
00404534 |. EB 05 JMP SHORT LIAOCACH.0040453B
00404536 |> 1BC0 SBB EAX,EAX
00404538 |. 83D8 FF SBB EAX,-1
0040453B |> 3BC3 CMP EAX,EBX
0040453D |. 75 0C JNZ SHORT LIAOCACH.0040454B
0040453F |. 8BC7 MOV EAX,EDI
00404541 |. 5F POP EDI
00404542 |. 5E POP ESI
00404543 |. 5D POP EBP
00404544 |. 5B POP EBX
00404545 |. 83C4 54 ADD ESP,54
00404548 |. C2 0400 RETN 4

我的註釋寫的還算清楚吧 ^_^,我再大概給你講解一下:
軟體的註冊碼是這樣計算出來的,機器碼中的各個字元的ASCII碼加上1500後除以62的餘數在密碼表中對應的字元,就是相應的註冊碼。
比如說我這裡的機器碼為xn2urkeUMwpNv5xZ,x的ASCII碼為78(十進制120) 78+5DC的值為654(即1620) 接著用1620除以3E(62)得商26余8,好的,我們從「密碼表」0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ的開始處向後數8下,就會到了8這個字元,嘿嘿,這就是x對應的註冊碼。
好的,我指出Delphi的註冊機(我仍將其寫為函數的形式):
function KeyGen(Name: String): String;
var
S:String[16];
P:String;
Key:String;
i,N,Z:integer;
begin
P:=\'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\';
if Length(Name)<16 then
Result:=\'機器碼必須為16位...\'
else
begin
S:=Name;
for i:=1 to 16 do
begin
N:=Ord(S);
N:=N+1500;
Z:= N mod 62;
Z:=Z+1;
Key:=Key+P[Z];
end;
Result:=Key;
end;
end;
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次