製作mIRC6.02序號產生器(給別人寫的初學者序號產生器教材) (14千字)

看雪資料發表於2015-11-15

暑假裡迷起了irc,所以拿到了mIRC就順便把它keygen了。我用的是mIRC 6.02版。此軟體採用的是使用者名稱/註冊碼的保護,在軟體的幫助選單裡有註冊選項。
    首先我們看看基本的註冊情況。填好相關的註冊碼和使用者名稱之後,點註冊會發現提示錯誤。之後自動清除了輸入的內容,但是還是可以繼續註冊。瞭解這些就夠了。
    現在開始我們的破解之旅。
    輸入好註冊碼和使用者名稱之後,進入trw,下斷點,bpx GetDlgItemTextA,bpx GetWindowTextA。
    因為這兩個是常用的獲取EDIT控制元件文字內容的函式啦。
    點註冊,發現沒有攔截下來。
    那麼就有兩種可能性了:第一,程式採用了其他的方法獲得文字內容,比如除了剛才的兩個斷點之外,還可以向EDIT控制元件傳送WM_GETTEXT訊息來獲得文字;第二,程式是用delphi編寫的,有自己獨立的獲取方式而不依靠windows提供的函式。
    無論是哪種可能性,都可以用一種方法來解決,那就是下斷點bpx hmemcpy。這個號稱是9x平臺下的萬能斷點,只要是輸入註冊碼的,都可以用這個斷點來攔截。
    下斷點,點註冊。終於攔截下來了。攔下後,下命令pmodule,回到mIRC程式裡,發現我們在call SendDlgItemMessageA的下面,原來它是用這個函式給對話方塊裡的EDIT控制元件傳送WM_GETTEXT訊息。

016F:004C688D 68A8A85700      PUSH    DWORD 0057A8A8 <==============以後名字存放的地點
016F:004C6892 68E7030000      PUSH    DWORD 03E7
016F:004C6897 6A0D            PUSH    BYTE +0D <====================WM_GETTEXT的值
016F:004C6899 6883000000      PUSH    DWORD 83
016F:004C689E FF7508          PUSH    DWORD [EBP+08] 
016F:004C68A1 E8284B0900      CALL    `USER32!SendDlgItemMessageA`
016F:004C68A6 688FAC5700      PUSH    DWORD 0057AC8F <=======+=======pmodule後我們就在這裡了
016F:004C68AB 68E7030000      PUSH    DWORD 03E7            |_______以後我們輸入的註冊碼
016F:004C68B0 6A0D            PUSH    BYTE +0D                      存放的地點
016F:004C68B2 6882000000      PUSH    DWORD 82
016F:004C68B7 FF7508          PUSH    DWORD [EBP+08]
016F:004C68BA E80F4B0900      CALL    `USER32!SendDlgItemMessageA`
016F:004C68BF 688FAC5700      PUSH    DWORD 0057AC8F <==============註冊碼作為引數二
016F:004C68C4 68A8A85700      PUSH    DWORD 0057A8A8 <==============姓名作為引數一
016F:004C68C9 E88FFBFFFF      CALL    004C645D <====================呼叫函式(關鍵的函式)!!
016F:004C68CE 85C0            TEST    EAX,EAX  <====================測試函式返回值
016F:004C68D0 0F84B7000000    JZ      NEAR 004C698D <===============返回值如果等於0
                                                                      就跳走(出錯的地方)

從我們看到的程式碼來看,004c645d處函式肯定是產生註冊碼並且進行比較的函式.因此我們應當跟進去,走到004C68C9的時候,F8跟進去.看到:
016F:004C645D 55              PUSH    EBP
016F:004C645E 8BEC            MOV      EBP,ESP
016F:004C6460 53              PUSH    EBX
016F:004C6461 56              PUSH    ESI
016F:004C6462 57              PUSH    EDI
016F:004C6463 8B750C          MOV      ESI,[EBP+0C]
016F:004C6466 8B5D08          MOV      EBX,[EBP+08]
016F:004C6469 BF440E5800      MOV      EDI,00580E44
016F:004C646E 6804010000      PUSH    DWORD 0104
016F:004C6473 6A00            PUSH    BYTE +00
016F:004C6475 57              PUSH    EDI
016F:004C6476 E8D55C0800      CALL    0054C150
016F:004C647B 83C40C          ADD      ESP,BYTE +0C
016F:004C647E 6804010000      PUSH    DWORD 0104
016F:004C6483 6A00            PUSH    BYTE +00
016F:004C6485 68480F5800      PUSH    DWORD 00580F48
016F:004C648A E8C15C0800      CALL    0054C150
016F:004C648F 83C40C          ADD      ESP,BYTE +0C
016F:004C6492 56              PUSH    ESI
016F:004C6493 57              PUSH    EDI
016F:004C6494 8BF7            MOV      ESI,EDI
016F:004C6496 8BFB            MOV      EDI,EBX
016F:004C6498 33C0            XOR      EAX,EAX
016F:004C649A 83C9FF          OR      ECX,BYTE -01
016F:004C649D F2AE            REPNE SCASB
016F:004C649F F7D1            NOT      ECX
016F:004C64A1 2BF9            SUB      EDI,ECX
016F:004C64A3 87F7            XCHG    ESI,EDI
016F:004C64A5 8BC7            MOV      EAX,EDI
016F:004C64A7 8BD1            MOV      EDX,ECX
016F:004C64A9 C1E902          SHR      ECX,02
016F:004C64AC F3A5            REP MOVSD
016F:004C64AE 8BCA            MOV      ECX,EDX
016F:004C64B0 83E103          AND      ECX,BYTE +03
016F:004C64B3 F3A4            REP MOVSB
016F:004C64B5 5F              POP      EDI
016F:004C64B6 5E              POP      ESI
016F:004C64B7 56              PUSH    ESI
016F:004C64B8 57              PUSH    EDI
016F:004C64B9 8BFE            MOV      EDI,ESI
016F:004C64BB BE480F5800      MOV      ESI,00580F48
016F:004C64C0 33C0            XOR      EAX,EAX
016F:004C64C2 83C9FF          OR      ECX,BYTE -01
016F:004C64C5 F2AE            REPNE SCASB
016F:004C64C7 F7D1            NOT      ECX
016F:004C64C9 2BF9            SUB      EDI,ECX
016F:004C64CB 87F7            XCHG    ESI,EDI
016F:004C64CD 8BC7            MOV      EAX,EDI
016F:004C64CF 8BD1            MOV      EDX,ECX
016F:004C64D1 C1E902          SHR      ECX,02
016F:004C64D4 F3A5            REP MOVSD
016F:004C64D6 8BCA            MOV      ECX,EDX
016F:004C64D8 83E103          AND      ECX,BYTE +03
016F:004C64DB F3A4            REP MOVSB
016F:004C64DD 5F              POP      EDI
016F:004C64DE 5E              POP      ESI
016F:004C64DF 68480F5800      PUSH    DWORD 00580F48
016F:004C64E4 57              PUSH    EDI
016F:004C64E5 E880FEFFFF      CALL    004C636A <==================call 後面緊跟test和跳轉的
016F:004C64EA 85C0            TEST    EAX,EAX                    就要留意了
016F:004C64EC 740A            JZ      004C64F8  <===eax=0就跳到失敗,所以在函式返回前不能
                                                        讓eax=0

016F:004C64EE B801000000      MOV      EAX,01 <========令eax=1,然後跳轉,不遠處就返回了,
016F:004C64F3 E991000000      JMP      004C6589        回到上面004c68ce的地方,上面要求
                                                        eax不等於0就註冊成功,這裡是1正好
                                                        滿足,所以這個004664E5大有文章.

前面的一大票程式碼看起來很難分析,其實暫時可以不管,因為這還是我們第一遍跟蹤.第一遍只要大概掌握程式碼的情況就可以了.我們注意到004C64E5 處的call和後面的test jz又構成了一個呼叫函式然後比較返回值的情況.而且它後面有mov eax,1 jmp 004c6589.這就可以肯定是判斷註冊碼是否合法的函式了.看看在call前面push 入棧的是哪些東西.在執行到0x4c64e4的時候,下命令d 580f48 可以看到註冊碼,d edi 看到名字.所以,這更加肯定了我們的推測,004c636a處的函式就是核心.在跟進去之前要記住,不能讓eax=0,不然就是註冊失敗的標誌.好了跟進來.

016F:004C636A 55              PUSH    EBP
016F:004C636B 8BEC            MOV      EBP,ESP
016F:004C636D 83C4F4          ADD      ESP,BYTE -0C
016F:004C6370 53              PUSH    EBX
016F:004C6371 56              PUSH    ESI
016F:004C6372 57              PUSH    EDI
016F:004C6373 8B750C          MOV      ESI,[EBP+0C]
016F:004C6376 FF7508          PUSH    DWORD [EBP+08]  <==========d *(ebp+8)發現這是名字
016F:004C6379 E8525F0800      CALL    0054C2D0 <====這個函式是幹什麼的?
016F:004C637E 59              POP      ECX <=====執行完函式到了這裡,看看eax裡是什麼?是名字的
016F:004C637F 83F805          CMP      EAX,BYTE +05 <==比較名字是不是大於5個字母      長度.
016F:004C6382 7307            JNC      004C638B
016F:004C6384 33C0            XOR      EAX,EAX    <==小於5就把eax清零,返回,就註冊失敗了.
016F:004C6386 E9C9000000      JMP      004C6454
016F:004C638B 6A2D            PUSH    BYTE +2D  <==0x2d就是字元"-"
016F:004C638D 56              PUSH    ESI  <==註冊碼
016F:004C638E E89D5E0800      CALL    0054C230  <==這個函式跟蹤後發現是返回"-"在註冊碼中
016F:004C6393 83C408          ADD      ESP,BYTE +08 的位置.如果你輸入的註冊碼不含"-"就失敗
016F:004C6396 8BD8            MOV      EBX,EAX
016F:004C6398 85DB            TEST    EBX,EBX
016F:004C639A 7507            JNZ      004C63A3
016F:004C639C 33C0            XOR      EAX,EAX
016F:004C639E E9B1000000      JMP      004C6454
016F:004C63A3 C60300          MOV      BYTE [EBX],00  <===把"-"替換成0,就是說把我們輸入的
016F:004C63A6 56              PUSH    ESI                註冊碼變成兩個字串
016F:004C63A7 E878ED0800      CALL    00555124      <===把註冊碼第一部分轉化成數值
                                                          (原來輸入的是字元)     
016F:004C63AC 59              POP      ECX
016F:004C63AD 8945FC          MOV      [EBP-04],EAX  <===把第一部分的數值放入這個地方
016F:004C63B0 C6032D          MOV      BYTE [EBX],2D  <===把註冊碼恢復成員來的樣子
016F:004C63B3 43              INC      EBX
016F:004C63B4 803B00          CMP      BYTE [EBX],00 
016F:004C63B7 7507            JNZ      004C63C0     
016F:004C63B9 33C0            XOR      EAX,EAX
016F:004C63BB E994000000      JMP      004C6454
016F:004C63C0 53              PUSH    EBX
016F:004C63C1 E85EED0800      CALL    00555124      <===把註冊碼第二部分轉化成數值
016F:004C63C6 59              POP      ECX
016F:004C63C7 8945F8          MOV      [EBP-08],EAX  <===存好
016F:004C63CA FF7508          PUSH    DWORD [EBP+08] <===姓名
016F:004C63CD E8FE5E0800      CALL    0054C2D0      <===取得姓名長度
016F:004C63D2 59              POP      ECX
016F:004C63D3 8945F4          MOV      [EBP-0C],EAX  <===把長度存好
016F:004C63D6 33C0            XOR      EAX,EAX        <===開始計算了
016F:004C63D8 33DB            XOR      EBX,EBX
016F:004C63DA BA03000000      MOV      EDX,03
016F:004C63DF 8B4D08          MOV      ECX,[EBP+08]
016F:004C63E2 83C103          ADD      ECX,BYTE +03  <===從姓名第三個字開始算
016F:004C63E5 3B55F4          CMP      EDX,[EBP-0C] 
016F:004C63E8 7D1C            JNL      004C6406
016F:004C63EA 0FB631          MOVZX    ESI,BYTE [ECX] <===取姓名裡的一個字元
016F:004C63ED 0FAF3485B8835600 IMUL    ESI,[EAX*4+005683B8] <==與一個數相乘儲存在esi中
016F:004C63F5 03DE            ADD      EBX,ESI <=累加起來    (這個數具體是什麼後面講)
016F:004C63F7 40              INC      EAX
016F:004C63F8 83F826          CMP      EAX,BYTE +26
016F:004C63FB 7E02            JNG      004C63FF
016F:004C63FD 33C0            XOR      EAX,EAX
016F:004C63FF 42              INC      EDX
016F:004C6400 41              INC      ECX
016F:004C6401 3B55F4          CMP      EDX,[EBP-0C] 
016F:004C6404 7CE4            JL      004C63EA  迴圈完成迴圈計算後,ebx裡面就是計算的結果
016F:004C6406 3B5DFC          CMP      EBX,[EBP-04]  比較ebx和開始獲得的註冊碼第一部分數值
016F:004C6409 7404            JZ      004C640F    相等就繼續計算第二部分
016F:004C640B 33C0            XOR      EAX,EAX      不然就清0 eax滾蛋
016F:004C640D EB45            JMP      SHORT 004C6454
016F:004C640F 33C0            XOR      EAX,EAX    <==第二部分開始了
016F:004C6411 33DB            XOR      EBX,EBX
016F:004C6413 BA03000000      MOV      EDX,03 
016F:004C6418 8B4D08          MOV      ECX,[EBP+08]
016F:004C641B 83C103          ADD      ECX,BYTE +03  <=還是從姓名的第3個字開始算
016F:004C641E 3B55F4          CMP      EDX,[EBP-0C]
016F:004C6421 7D23            JNL      004C6446
016F:004C6423 0FB631          MOVZX    ESI,BYTE [ECX]  <=取姓名裡的一個字
016F:004C6426 0FB679FF        MOVZX    EDI,BYTE [ECX-01] <=取前一個字
016F:004C642A 0FAFF7          IMUL    ESI,EDI          <=相乘
016F:004C642D 0FAF3485B8835600 IMUL    ESI,[EAX*4+005683B8] <=再與另一個數相乘
016F:004C6435 03DE            ADD      EBX,ESI <=累加        (和上面的那個一樣後面講)
016F:004C6437 40              INC      EAX
016F:004C6438 83F826          CMP      EAX,BYTE +26
016F:004C643B 7E02            JNG      004C643F
016F:004C643D 33C0            XOR      EAX,EAX
016F:004C643F 42              INC      EDX
016F:004C6440 41              INC      ECX
016F:004C6441 3B55F4          CMP      EDX,[EBP-0C]
016F:004C6444 7CDD            JL      004C6423    迴圈計算
016F:004C6446 3B5DF8          CMP      EBX,[EBP-08] 比較計算結果,和註冊碼第二部分數值
016F:004C6449 7404            JZ      004C644F    相同則跳到成功標誌
016F:004C644B 33C0            XOR      EAX,EAX
016F:004C644D EB05            JMP      SHORT 004C6454
016F:004C644F B801000000      MOV      EAX,01      設立註冊碼比較成功標誌
016F:004C6454 5F              POP      EDI
016F:004C6455 5E              POP      ESI
016F:004C6456 5B              POP      EBX
016F:004C6457 8BE5            MOV      ESP,EBP
016F:004C6459 5D              POP      EBP
016F:004C645A C20800          RET      08

好了,現在講一講上面兩次出現的[EAX*4+005683B8]是什麼意思.
在跟蹤到那個乘法的時候,下d 005683B8,會看到資料欄裡是這個樣子
0B000000 06000000 11000000 0C000000
0C000000 0E000000 05000000 0C000000
10000000 0A000000 0B000000 06000000
0E000000 0E000000 04000000 0B000000
06000000 0E000000 0E000000 04000000
0B000000 09000000 0C000000 0B000000
0A000000 08000000 0A000000 0A000000
10000000 08000000 04000000 06000000
0A000000 0C000000 10000000 08000000
0A000000 04000000 10000000 00000000
發現資料都是每4位元組出現一次,這就是為什麼用eax*4了.其實005683B8這個地址裡出現的資料是固定的,就是說這是個字典,每次計算註冊碼的時候到裡面來查一個數字然後跟名字的一個字母做乘法運算.那麼我怎麼才能知道這個字典是多大呢?那就看
016F:004C63F8 83F826          CMP      EAX,BYTE +26

016F:004C6438 83F826          CMP      EAX,BYTE +26
都是把eax和0x26,也就是38來比較,所以一共有39個資料.就是除了0之外的那些.

至此,我們完全摸清了,mIRC的註冊碼的計算情況.當我們執行到016F:004C6406和016F:004C6446的時候各做一次? ebx顯示出十進位制值,然後把兩個值用"-"連結起來就是我們輸入名字所對應的註冊碼了,比如我的lllaaa[BCG]對應7591-674568.

好了,既然搞清楚了演算法,寫序號產生器也就容易了.下面我貼出序號產生器的c語言原始碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char *argv[])
{
    char dict[39]=
    {
        0x0b,0x06,0x11,0x0c,0x0c,0x0e,0x05,0x0c,0x10,0x0a,
        0x0b,0x06,0x0e,0x0e,0x04,0x0b,0x06,0x0e,0x0e,0x04,
        0x0b,0x09,0x0c,0x0b,0x0a,0x08,0x0a,0x0a,0x10,0x08,
        0x04,0x06,0x0a,0x0c,0x10,0x08,0x0a,0x04,0x10
    };
    char name[255];
    printf("Input your name please:");
    gets(name);
    int length=strlen(name);
    if (length>=39)
        {
            printf("\nYour name is longer than 39 chars,please choose another one.");
            return 1;
        }
    int i=0;
    long serial1=0,serial2=0;
    for (i=0;i<length-3;i++)
        {
            serial1+=name[i+3]*dict[i];
            serial2+=name[i+3]*name[i+2]*dict[i];
        }
        printf("The serial number is:%d-%d",serial1,serial2);
        return 1;
}

本來源程式是支援名字超過39位元組的,但是我為了程式設計的簡單,就做了限制.

相關文章