一篇破解教程-----面向初學者 (15千字)

看雪資料發表於2001-04-01

一篇破解教程-----面向初學者(轉載請保持完整,且不得用於商業用途)
作者: Fpc
工具:Trw2000,W32dasm 8.93

軟體名稱:五子棋99 Ver 2.0
作    者: 華東計算技術研究所  蔣祥剛
檔案大小:497KB
授權方式:共享軟體
使用平臺:Win95/98

軟體簡介:一個五子棋遊戲軟體,與常見的同類軟體差不多,棋力不強

軟體來源:《網頁設計素材2000》(3CD)中的遊戲目錄
下載地址:暫時不可用,大家耐心等

見到需要輸入註冊名和註冊碼的軟體就忍不住去按 ^D,可能是破解綜合症吧 ~_~。在同事的機器上發現了一個五子棋的遊戲,玩了幾局,居然要註冊。簡直是*#*@&&,也不看看棋力有多差,都不忍心去破。想一想,權當是一個CrackMe,開始吧。
    

多說兩句,大多數五子棋軟體的棋力都不行,都是亂走棋,只要你看過一兩本棋譜,對付它們不成問題。記得在學校時接觸過一個“五子棋大師 二”,體積很大,但棋力非常強。其實優秀的棋類軟體應該是演算法和棋譜管理的結合體。象棋的“將族3”就很厲害,我連“小豬頭”都對付不了,自己也是太笨:-)

用TRW2000(感謝LTT和ZNH,我決定註冊,不知註冊費是多少¥)載入FIVE,在“關於”中要求輸入註冊碼。這是一個典型的“註冊名”加“註冊碼”註冊形式。在姓名中輸入“Fpc”,註冊碼中輸入“494949”(在下面的分析中你會看到,正好滿足了6位註冊碼的條件)。
  

^N到TRW,下bpx hmemcpy(萬能中斷);^N返回,單擊“註冊”,立刻到TRW中,按一次^N(因為有兩個輸入的文字框),還會回到TRW中;
  下BD *,按幾次F12,你會返回到下面,見分析:

:0045E884 8B80E0020000            mov eax, dword ptr [eax+000002E0]
:0045E88A E81DC6FCFF              call 0042AEAC                <==== 這次呼叫取得 註冊名
:0045E88F 8B45FC                  mov eax, dword ptr [ebp-04]
:0045E892 50                      push eax

* Possible StringData Ref from Code Obj ->"Name"
                                  |
:0045E893 B92CE94500              mov ecx, 0045E92C

* Possible StringData Ref from Code Obj ->"Register"
                                  |
:0045E898 BA3CE94500              mov edx, 0045E93C
:0045E89D A1649A4600              mov eax, dword ptr [00469A64]
:0045E8A2 8B18                    mov ebx, dword ptr [eax]
:0045E8A4 FF5304                  call [ebx+04]                <==== 寫入 ini 檔案
:0045E8A7 8D55FC                  lea edx, dword ptr [ebp-04]
:0045E8AA A15C9A4600              mov eax, dword ptr [00469A5C]
:0045E8AF 8B80E8020000            mov eax, dword ptr [eax+000002E8]
:0045E8B5 E8F2C5FCFF              call 0042AEAC                <==== 取得輸入的註冊碼
:0045E8BA 8B45FC                  mov eax, dword ptr [ebp-04]        《=== 你會返回到這裡
:0045E8BD 50                      push eax

* Possible StringData Ref from Code Obj ->"RegNo"
                                  |
:0045E8BE B950E94500              mov ecx, 0045E950

* Possible StringData Ref from Code Obj ->"Register"
                                  |
:0045E8C3 BA3CE94500              mov edx, 0045E93C
:0045E8C8 A1649A4600              mov eax, dword ptr [00469A64]
:0045E8CD 8B18                    mov ebx, dword ptr [eax]
:0045E8CF FF5304                  call [ebx+04]                <==== 寫入 ini 檔案
:0045E8D2 E855FDFFFF              call 0045E62C                <==== Call+Cmp+Jmp 的註冊碼判斷模式非常常見
:0045E8D7 803D609A460000          cmp byte ptr [00469A60], 00        <==== [469A60]是個標誌,如果不為0,則註冊成功
:0045E8DE 750C                    jne 0045E8EC

* Possible StringData Ref from Code Obj ->"名字和註冊號不匹配!"    <==== 可惜在 TRW 和 W32dasm 的正文中都無法看到漢字 :-(
                                  |
:0045E8E0 B860E94500              mov eax, 0045E960
:0045E8E5 E876CAFEFF              call 0044B360                <==== 呼叫MessageBoxA
:0045E8EA EB14                    jmp 0045E900

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E8DE(C)
|

* Possible StringData Ref from Code Obj ->"恭喜你已經成功註冊!"
                                  |
:0045E8EC B88CE94500              mov eax, 0045E98C
:0045E8F1 E86ACAFEFF              call 0044B360                <==== 同樣呼叫MessageBoxA
:0045E8F6 A15C9A4600              mov eax, dword ptr [00469A5C]
:0045E8FB E85C51FEFF              call 00443A5C

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E8EA(U)
|
:0045E900 33C0                    xor eax, eax
:0045E902 5A                      pop edx
:0045E903 59                      pop ecx
:0045E904 59                      pop ecx
:0045E905 648910                  mov dword ptr fs:[eax], edx
:0045E908 681DE94500              push 0045E91D                <==== 這裡我想不明白是如何產生的,這種呼叫(跳轉)在W32dasm中不會給出提示
                                    <==== 這個push 和之後的 ret 會越過下面的兩個 jmp
* Referenced by a (U)nconditional or (C)onditional Jump at Address:    <==== 跳到45E91D去執行,明白其中道理的請告訴我
|:0045E91B(U)
|
:0045E90D 8D45FC                  lea eax, dword ptr [ebp-04]
:0045E910 E8AB4FFAFF              call 004038C0
:0045E915 C3                      ret


:0045E916 E9E149FAFF              jmp 004032FC
:0045E91B EBF0                    jmp 0045E90D
:0045E91D 5B                      pop ebx                <====
:0045E91E 59                      pop ecx
:0045E91F 5D                      pop ebp
:0045E920 C3                      ret


  很明顯,:0045E8D2 處的呼叫的 call 0045E62C 是判斷註冊碼的關鍵。下bpx 45E8D2,^N返回到FIVE中,用原來的資訊再註冊。這次會直接中斷到 45E8D2,按F8追進這個call:

* Referenced by a CALL at Addresses:
|:0045E8D2  , :0045E9DA 
|
:0045E62C 55                      push ebp
:0045E62D 8BEC                    mov ebp, esp
:0045E62F 81C4CCFDFFFF            add esp, FFFFFDCC
:0045E635 53                      push ebx
:0045E636 56                      push esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E5D0(C)
|
:0045E637 57                      push edi
:0045E638 33C0                    xor eax, eax
:0045E63A 8985CCFDFFFF            mov dword ptr [ebp+FFFFFDCC], eax
:0045E640 33C0                    xor eax, eax
:0045E642 55                      push ebp
:0045E643 68A0E74500              push 0045E7A0
:0045E648 64FF30                  push dword ptr fs:[eax]
:0045E64B 648920                  mov dword ptr fs:[eax], esp
:0045E64E 6A00                    push 00000000
:0045E650 8D85CCFDFFFF            lea eax, dword ptr [ebp+FFFFFDCC]
:0045E656 50                      push eax

* Possible StringData Ref from Code Obj ->"Name"
                                  |
:0045E657 B9B8E74500              mov ecx, 0045E7B8

* Possible StringData Ref from Code Obj ->"register"
                                  |
:0045E65C BAC8E74500              mov edx, 0045E7C8
:0045E661 A1649A4600              mov eax, dword ptr [00469A64]
:0045E666 8B18                    mov ebx, dword ptr [eax]
:0045E668 FF13                    call dword ptr [ebx]            <==== 從ini檔案中取得註冊名
:0045E66A 8B95CCFDFFFF            mov edx, dword ptr [ebp+FFFFFDCC]    \
:0045E670 8D85D0FDFFFF            lea eax, dword ptr [ebp+FFFFFDD0]    |
:0045E676 B9FF000000              mov ecx, 000000FF            |
:0045E67B E89854FAFF              call 00403B18                |
:0045E680 8D95D0FDFFFF            lea edx, dword ptr [ebp+FFFFFDD0]    |
:0045E686 8D459B                  lea eax, dword ptr [ebp-65]        \ 將註冊名資訊存放到 ebp-64到ebp處,ebp-65是註冊名長度
                                    /
* Referenced by a (U)nconditional or (C)onditional Jump at Address:    |
|:0045E622(C)                                |
|                                    |
:0045E689 B164                    mov cl, 64                |
:0045E68B E8F041FAFF              call 00402880                  /
:0045E690 6A00                    push 00000000
:0045E692 8D85CCFDFFFF            lea eax, dword ptr [ebp+FFFFFDCC]
:0045E698 50                      push eax

* Possible StringData Ref from Code Obj ->"RegNo"
                                  |
:0045E699 B9DCE74500              mov ecx, 0045E7DC

* Possible StringData Ref from Code Obj ->"register"
                                  |
:0045E69E BAC8E74500              mov edx, 0045E7C8
:0045E6A3 A1649A4600              mov eax, dword ptr [00469A64]
:0045E6A8 8B18                    mov ebx, dword ptr [eax]
:0045E6AA FF13                    call dword ptr [ebx]            <==== 從ini檔案中取得註冊碼
:0045E6AC 8B95CCFDFFFF            mov edx, dword ptr [ebp+FFFFFDCC]    \
:0045E6B2 8D85D0FDFFFF            lea eax, dword ptr [ebp+FFFFFDD0]    |
:0045E6B8 B9FF000000              mov ecx, 000000FF            |
:0045E6BD E85654FAFF              call 00403B18                \ 將註冊名資訊存放到 ebp-c9到ebp-66處,ebp-ca是註冊名長度
:0045E6C2 8D95D0FDFFFF            lea edx, dword ptr [ebp+FFFFFDD0]    /
:0045E6C8 8D8536FFFFFF            lea eax, dword ptr [ebp+FFFFFF36]    |
:0045E6CE B164                    mov cl, 64                |
:0045E6D0 E8AB41FAFF              call 00402880                  /
:0045E6D5 8A459B                  mov al, byte ptr [ebp-65]
:0045E6D8 84C0                    test al, al                <==== 檢查註冊名長度是否為0
:0045E6DA 7409                    je 0045E6E5
:0045E6DC 80BD36FFFFFF06          cmp byte ptr [ebp+FFFFFF36], 06    <==== 檢查註冊碼長度是否為6
:0045E6E3 740C                    je 0045E6F1

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6DA(C)
|
:0045E6E5 C605609A460000          mov byte ptr [00469A60], 00        <==== 錯誤則置標誌為0
:0045E6EC E996000000              jmp 0045E787                <==== 跳到下面,返回

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6E3(C)
|
:0045E6F1 8BF0                    mov esi, eax
:0045E6F3 81E6FF000000            and esi, 000000FF
:0045E6F9 85F6                    test esi, esi
:0045E6FB 7C21                    jl 0045E71E
:0045E6FD 46                      inc esi                <==== esi為註冊名長度加1
:0045E6FE 8D4D9B                  lea ecx, dword ptr [ebp-65]        <==== ecx指向 註冊名長度位元組+註冊名
:0045E701 8D9DD1FEFFFF            lea ebx, dword ptr [ebp+FFFFFED1]    <==== ebx指向欲生成的註冊碼地址

* Referenced by a (U)nconditional or (C)onditional Jump at Address:    <==== 下面這個迴圈生成註冊碼,設為 註冊碼A
|:0045E71C(C)
|
:0045E707 33C0                    xor eax, eax
:0045E709 8A01                    mov al, byte ptr [ecx]
:0045E70B BF0A000000              mov edi, 0000000A            <==== 除數為a,即為 10(十進位制)
:0045E710 33D2                    xor edx, edx
:0045E712 F7F7                    div edi                <==== 相除後,edx中為餘數
:0045E714 80C230                  add dl, 30                <==== 變為 ASCII 碼
:0045E717 8813                    mov byte ptr [ebx], dl        <==== 按先後順序存放於 [ebx]
:0045E719 43                      inc ebx
:0045E71A 41                      inc ecx
:0045E71B 4E                      dec esi
:0045E71C 75E9                    jne 0045E707                <==== 未完則繼續

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6FB(C)
|
:0045E71E 8A459B                  mov al, byte ptr [ebp-65]
:0045E721 3C06                    cmp al, 06                <==== 註冊名長度超過6位元組則不需要再處理
:0045E723 732F                    jnb 0045E754
:0045E725 33C9                    xor ecx, ecx
:0045E727 8AC8                    mov cl, al
:0045E729 41                      inc ecx
:0045E72A 83F906                  cmp ecx, 00000006            <==== 多此一舉!!
:0045E72D 7F25                    jg 0045E754
:0045E72F 8D9C0DD0FEFFFF          lea ebx, dword ptr [ebp+ecx-00000130]    <==== 從 註冊碼A 的最後一個數開始補足7位

* Referenced by a (U)nconditional or (C)onditional Jump at Address:    <==== 下面這個迴圈將生成的註冊碼A 補足為7位
|:0045E752(C)
|
:0045E736 33C0                    xor eax, eax
:0045E738 8A03                    mov al, byte ptr [ebx]        <==== 取得[ebx]處的數字
:0045E73A 40                      inc eax                <==== 加一
:0045E73B 83E830                  sub eax, 00000030
:0045E73E BE0A000000              mov esi, 0000000A
:0045E743 33D2                    xor edx, edx
:0045E745 F7F6                    div esi                <==== 相除,取模到edx
:0045E747 80C230                  add dl, 30                <==== 變為 ASCII 碼
:0045E74A 885301                  mov byte ptr [ebx+01], dl        <==== 存放到[ebx+1]處
:0045E74D 41                      inc ecx
:0045E74E 43                      inc ebx
:0045E74F 83F907                  cmp ecx, 00000007            <==== 不足7位則繼續
:0045E752 75E2                    jne 0045E736


    可以分析出,註冊碼支援所有可列印字元和漢字;將 註冊碼A 補足為7位的規律是:將最後一位數加一作為下一位數,如果9加一則變為0,依次處理。例如:
    註冊碼A=285,則補足為 2856789; 註冊碼A=3619,則補足為 3619012。

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045E723(C), :0045E72D(C)
|
:0045E754 C685D8FEFFFF00          mov byte ptr [ebp+FFFFFED8], 00    <==== 很粗暴的在第八個位元組處置0,之後的數字被捨棄
:0045E75B C605609A460001          mov byte ptr [00469A60], 01        <==== 預置註冊標誌為1(成功)
:0045E762 B906000000              mov ecx, 00000006            <==== 比較的位元組數
:0045E767 8D85D2FEFFFF            lea eax, dword ptr [ebp+FFFFFED2]    <==== 算出的註冊碼,注意第一位被捨棄
:0045E76D 8D9537FFFFFF            lea edx, dword ptr [ebp+FFFFFF37]    <==== 你輸入的註冊碼

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E785(C)
|
:0045E773 8A18                    mov bl, byte ptr [eax]        
:0045E775 3A1A                    cmp bl, byte ptr [edx]
:0045E777 7409                    je 0045E782
:0045E779 C605609A460000          mov byte ptr [00469A60], 00        <==== 比較失敗則置標誌為0
:0045E780 EB05                    jmp 0045E787

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E777(C)
|
:0045E782 42                      inc edx
:0045E783 40                      inc eax
:0045E784 49                      dec ecx
:0045E785 75EC                    jne 0045E773                <==== 不到6位則繼續

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045E6EC(U), :0045E780(U)
|
:0045E787 33C0                    xor eax, eax
:0045E789 5A                      pop edx
:0045E78A 59                      pop ecx
:0045E78B 59                      pop ecx
:0045E78C 648910                  mov dword ptr fs:[eax], edx
:0045E78F 68A7E74500              push 0045E7A7

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E7A5(U)
|
:0045E794 8D85CCFDFFFF            lea eax, dword ptr [ebp+FFFFFDCC]
:0045E79A E82151FAFF              call 004038C0
:0045E79F C3                      ret


:0045E7A0 E9574BFAFF              jmp 004032FC
:0045E7A5 EBED                    jmp 0045E794
:0045E7A7 5F                      pop edi
:0045E7A8 5E                      pop esi
:0045E7A9 5B                      pop ebx
:0045E7AA 8BE5                    mov esp, ebp
:0045E7AC 5D                      pop ebp
:0045E7AD C3                      ret                    <==== 返回


上面的判斷過程用C表示:

int Success;

Registration()
{
    int LenName, LenCode;
    char UserName[100], RegCode[100], InputRegCode[6];
    int i;

    GetInfo( &UserName, LenName);        /*  取得註冊資訊 */
    GetInfo( &InputRegCode, LenCode);    /*         */
    
    if ( LenName==0 || LenCode!=6 )
    {
        Success=0;
        return;
    }
    
    RegCode[0]= LenName%10;
    for( i=0; i<LenName; i++)
        RegCode[i+1]= UserName[i]%10;

    if ( LenName<6 )
        for( i=0; i<6-LenName; i++)
            RegCode[i+LenName+1]= (RegCode[i+lenName]+1)%10;

    RegCode[7]='\0';
    Success=1;
    if (!strcmp( &RegCode, &InputRegCode))
        Success=0;
    return;
}

這基本上就是序號產生器,但沒有必要。FIVE的註冊形式比較簡單,也未採取保護措施,容易分析,只要當作一個CrackMe就好。
    

破解完成加上這篇教程,一看時鐘,ONLY 9:30,還作點什麼的呢?這時有人滿嘴酒氣的衝進來,莫名其妙的問我是不是駭客,真是“好事不出門,壞事***”:大眾根本就不懂HAKKER,CRAKKER是什麼含義,也不能解釋,真是掃興。這時抬頭看牆上的時鐘,CALL 11:30!明天早晨又起不來了。“我單知道,TRW和SI會停系統時鐘的,可憐我家的機器阿毛.......”(臺下口號聲此起彼伏:打倒唐僧),只有謝幕了。 



@20001/03/21

相關文章