皮兄,網際金典3的InstallShield序列號破解。 (9千字)

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

網際金典3的InstallShield序列號破解。

受皮特陳兄指使,也不知從哪兒弄來個網際金典3的不完全版,非得說要我們按“典型”的InstallShield破解法來找個序列號不可。臨危受命,不可不從,誰叫這位FCG的老大讓我們“難望其項背”呢?好,小貓下了半夜,睡一覺後起床穿衣洗臉刷牙吃飯後……開始!

網際金典安裝需要序列號,簡單的一小段,似乎是InstallShield安裝的典型。於是先用ISDCC2反編譯setup.ins到一個檔案中,開啟這個檔案,找開始的函式宣告部分(為什麼不找出錯資訊呢?原因……嘿嘿,宣告部分在前面嘛,容易看見。),果然有下面的幾行:

……
    prototype InstDll.RWCheckSerialNo(LIST, LIST, LIST);
    prototype InstDll.InstallTcFont(LIST);
……


這說明了什麼?越來越多的軟體不把序列號的檢驗過程放在InstallShield的指令碼檔案中,大概是免得反編譯一下就看出演算法來了,因此多多少少也找些DLL來把過程隱藏在裡面。這裡就說明SETUP程式需要用到一個叫InstDll.dll檔案中的RWCheckSerialNo過程。
下一步,不用找什麼出錯資訊了,直接在檔案中查詢字串“RWCheckSerialNo”,一下就找到了,下面是相關的上下文內容:

    // ------------- FUNCTION function95 --------------
    function function95(pString0, pString1, pString2)
        number lNumber0;
        number lNumber1;
        number lNumber2;
        number lNumber3;
        number lNumber4;
        string lString0;
    begin

label812:
00F164:00BA:        AddressString(pString0);
00F169:0021:        lNumber0 = LAST_RESULT;
00F171:00BA:        AddressString(pString1);
00F176:0021:        lNumber1 = LAST_RESULT;
00F17E:00BA:        AddressString(pString2);
00F183:0021:        lNumber2 = LAST_RESULT;
00F18B:0125:        lString0 = SUPPORTDIR ^ "INSTDLL.DLL";
00F1A1:00B2:        UseDLL(lString0);

//Usedll,說明INSTDLL.DLL是透過loadlibrarya動態載入的。

00F1A6:00B4:        InstDll.RWCheckSerialNo(lNumber0, lNumber1, lNumber2);
00F1B4:0021:        lNumber3 = LAST_RESULT;
00F1BC:00B3:        UnUseDLL(lString0);

//解除安裝INSTDLL.DLL。

00F1C1:0128:        lNumber4 = lNumber3 <= 0;    

//這句話應該說明一下。其實應該是lNumber4 = (lNumber3 <= 0),也就是說如果lNumber3小於等於0,則這個邏輯式為真,lNumber4的值就是1,下面的這個IF就會直接返回,不會跳轉到正確的label814。――這裡也告訴我們,RWCheckSerialNo要返回大於0的值才表示序列號透過檢驗,這點對於等會兒分析INSTDLL.DLL的程式碼是很重要的。

00F1D3:0022:        if (lNumber4 = 0) then
                        goto label814;
                    endif;
00F1E1:012F:        return(0);
00F1EE:012F:        return(1);
00F1F7:00B8:        return;
    end;

呼叫這個FUNCTION95的程式段也可以找到:

label10:
00112E:00B5:        function115();
001136:0021:        lNumber0 = LAST_RESULT;
00113E:0128:        lNumber4 = lNumber0 = 12;
001150:0022:        if (lNumber4 = 0) then
                        goto label12;
                    endif;
00115E:002C:        goto label10;

label11:
001167:00B5:        function116();
00116F:0021:        lNumber0 = LAST_RESULT;
001177:0128:        lNumber4 = lNumber0 = 12;
001189:0022:        if (lNumber4 = 0) then
                        goto label13;
                    endif;
001197:002C:        goto label11;
00119C:002C:        goto label14;

label12:
0011A5:00B5:        function95(string11, string6, string7);      //檢驗序列號
0011B6:0021:        lNumber4 = LAST_RESULT;
0011BE:0128:        lNumber4 = lNumber4 = 0;
0011D0:0022:        if (lNumber4 = 0) then              //錯則顯示提示
                        goto label14;
                    endif;
0011DE:0112:        StrLoadString("", "MSG_INFO", lString1);
0011F1:003B:        SetDialogTitle(4, lString1);
0011FB:0112:        StrLoadString("", "ERROR_CHECK_SERIAL_NO", lString1);
00121B:002A:        MessageBox(lString1, -65535);
001225:002C:        goto label12;

好了,setup.ins裡已經沒什麼地方可以做手腳了,執行setup.exe,在Windows\temp\_istmp0.dir下能找到一個instdll.dll檔案,哈哈,才23k,考慮到編譯時的無用程式碼,核心檢驗函式無論怎樣都不會太複雜。不管怎樣,先複製出來反彙編一下吧!
instdll.dll輸出函式RWCheckSerialNo的地址是1000(參看以下程式碼),這得先記住。
功力高的話可以直接後分析反彙編後的程式碼,如果像我這樣不怎麼樣的“低”手,就只有動態跟蹤了。SETUP本身的框架比較複雜,hmemcpy這種斷點只會把局面弄得更復雜。前面不是說了instdll.dll是動態載入的嗎?於是執行SETUP.EXE後在序列號輸入介面處切入SOFTICE,下斷bpx loadlibrarya,回來按下一步馬上斷掉。
按幾次F12返回到高層,用MOD INSTDLL看看剛載入的INSTDLL.DLL地址,我這裡是01A50000,加上RWCheckSerialNo函式的偏移1000,u 1A51000便是RWCheckSerialNo的入口,下斷bpx 0167:01a51000。
下面就是序列號的分析過程了:

Exported fn(): RWCheckSerialNo - Ord:0004h
:10001000 83EC10                  sub esp, 00000010
:10001003 56                      push esi
:10001004 57                      push edi
:10001005 8B7C241C                mov edi, dword ptr [esp+1C]    //輸入的序列號地址。
:10001009 33F6                    xor esi, esi
:1000100B 57                      push edi
:1000100C E8FF000000              call 10001110            //跟進去。
:10001011 83C404                  add esp, 00000004
:10001014 85C0                    test eax, eax            //如果懶得跟,改這裡即可。
:10001016 740D                    je 10001025
:10001018 B801000000              mov eax, 00000001        //正確標誌。
:1000101D 5F                      pop edi
:1000101E 5E                      pop esi
:1000101F 83C410                  add esp, 00000010
:10001022 C20C00                  ret 000C

下面是10001110處的內容:

* Referenced by a CALL at Addresses:
|:1000100C  , :100011AC 
|
:10001110 83EC50                  sub esp, 00000050
:10001113 56                      push esi
:10001114 8B742458                mov esi, dword ptr [esp+58]
:10001118 56                      push esi

* Reference To: KERNEL32.lstrlenA, Ord:029Ch
                                  |
:10001119 FF15AC810010            Call dword ptr [100081AC]
:1000111F 83F80D                  cmp eax, 0000000D
:10001122 7407                    je 1000112B

//檢測輸入的序列號長度必須為0x0D個字元,也就是13個。

:10001124 33C0                    xor eax, eax
:10001126 5E                      pop esi
:10001127 83C450                  add esp, 00000050
:1000112A C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001122(C)
|
:1000112B 8D442404                lea eax, dword ptr [esp+04]
:1000112F 56                      push esi
:10001130 50                      push eax

* Reference To: KERNEL32.lstrcpyA, Ord:0296h
                                  |
:10001131 FF1548810010            Call dword ptr [10008148]

//複製一下以備分析,此時ESP+4指向輸入的序列號。

:10001137 0FBE542404              movsx edx, byte ptr [esp+04]
:1000113C 0FBE4C2407              movsx ecx, byte ptr [esp+07]
:10001141 8BC2                    mov eax, edx
:10001143 2BC1                    sub eax, ecx
:10001145 83F8FF                  cmp eax, FFFFFFFF
:10001148 7407                    je 10001151

//這段說明序列號的第四個字元必須比第一個字元大1(Ascii碼)。

:1000114A 33C0                    xor eax, eax
:1000114C 5E                      pop esi
:1000114D 83C450                  add esp, 00000050
:10001150 C3                      ret


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001148(C)
|
:10001151 0FBE442408              movsx eax, byte ptr [esp+08]
:10001156 2BD0                    sub edx, eax
:10001158 83FA04                  cmp edx, 00000004
:1000115B 7407                    je 10001164

//這段說明序列號的第一個字元必須比第五個字元大4(Ascii碼)。

:1000115D 33C0                    xor eax, eax
:1000115F 5E                      pop esi
:10001160 83C450                  add esp, 00000050
:10001163 C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000115B(C)
|
:10001164 0FBE442405              movsx eax, byte ptr [esp+05]
:10001169 0FBE4C2406              movsx ecx, byte ptr [esp+06]
:1000116E 2BC1                    sub eax, ecx
:10001170 83F8F8                  cmp eax, FFFFFFF8
:10001173 7407                    je 1000117C


//這段說明序列號的第三個字元必須比第二個字元大8(Ascii碼)。

:10001175 33C0                    xor eax, eax
:10001177 5E                      pop esi
:10001178 83C450                  add esp, 00000050
:1000117B C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001173(C)
|
:1000117C 8D442409                lea eax, dword ptr [esp+09]

* Possible StringData Ref from Data Obj ->"lqhyhtlj"
                                  |
:10001180 6850500010              push 10005050
:10001185 50                      push eax

* Reference To: KERNEL32.lstrcmpiA, Ord:0293h
                                  |
:10001186 FF154C810010            Call dword ptr [1000814C]

//然後從第六個字元開始的八個字元必須是“lqhyhtlj”

:1000118C 5E                      pop esi
:1000118D 83F801                  cmp eax, 00000001
:10001190 1BC0                    sbb eax, eax
:10001192 83C450                  add esp, 00000050
:10001195 F7D8                    neg eax
:10001197 C3                      ret

於是我們可以捏造一個我們可以用的序列號,我捏的是“81994lqhyhtlj”,為保險一點又捏造了一個“e19falqhyhtlj”,安裝時填入,果然都透過了(出現了下一步的自定義安裝路徑的對話方塊)。
還有一點,instdll.dll在你輸入的字串長度為14時還有另外一串更復雜的檢驗演算法,我下的不是完全安裝版,沒法子判斷到底哪個才是正宗的,或者兩個都是?――這就得靠皮特陳兄用正式安裝版來檢驗檢驗了。^_^

相關文章