破解NetScanTools Pro 2000及其InstallShield指令碼破解(其實指令碼沒破成) (18千字)

看雪資料發表於2001-03-30

破解NetScanTools Pro 2000及其InstallShield指令碼破解(不完整)

Passion

我幾個星期前曾受被人尊稱為“皮大爺”的peterchen老兄的語無倫次的鞭策,於是也好高騖遠地想研究研究InstallShield的指令碼破解,正巧不久前見了洋白菜兄的InstallShield指令碼反編譯教程,覺得比一個位元組一個位元組地研究INS檔案的編譯後程式碼要方便多了,因而也就偷了許多懶。――NetScanTools Pro 2000是一套功能非常全面的網路掃描監控工具,Northwest Performance公司開發,網址是http://www.nwpsw.com/,不過此處沒完整版下載。
NetScanTools Pro 2000安裝時需要輸入序列號,直接破據說非常麻煩,一個比較好的法子是從INS檔案入手。我在洋白菜兄的主頁crackbest.com上下了一個DIS(DeInstallShield),用它反編譯setup.ins檔案感覺還可以。
NetScanTools Pro 2000安裝程式會詢問姓名公司序列號,填完按NEXT後會出現一個框要你確認填的是否正確,按“Yes”後才進行序列號檢測,錯則彈出對話方塊說“Invalid Serial Number。”沒的說,用DIS反編譯其SETUP.INS,在結果中找“Invalid Serial Number”的MSGREF訊息字串引用,很快就找到了下面的一段:

<LABEL_0038> REF: 00000F72
  |
00002044: 00B6  START OF FUNCTION (2*StrLocals + 3*NumLocals)
00002054: 0013  StrVar[001C] = ""
0000205C: 0013  StrVar[001D] = ""
00002064: 0013  StrLocal[0001] = ""
0000206C: 0013  StrLocal[0002] = ""
00002074: 0002  Disable (00000032)

<LABEL_0039> REF: 00002147
  |
0000207F: 0021  NumLocal[0002] = 00000000
0000208D: 0129  WHILE (NumLocal[0002] = 00000000)
000020AF: 00B5        NumLocal[0001] = SdRegisterUserEx_[LABEL_00B0] (StrLocal[0001],StrLocal[0002],StrVar[001C],StrVar[001D],StrVar[001E])

000020CE: 00B5        NumLocal[0002] = SdConfirmRegistration_[LABEL_00E5] (StrLocal[0001],StrVar[001C],StrVar[001D],StrVar[001E],00000000)
000020EF: 0000  ENDWHILE

//以上這個WHILE就是讓你輸入序列號並確認的。

00002108: 0128  IF (Call Function_006F_[LABEL_004D] != 00000001) THEN
~~~~這裡是不等於號
00002128: 002A        MessageBox ("Invalid Serial Number",SEVERE)
//在此處引用MSG字串。
00002147: 002C        Goto (LABEL_0039)
00002148: 0000  ENDIF

//然後呼叫LABEL_004D處的Function_006F來驗證序列號,如果返回值不是1就表示出錯,出現出錯對話方塊。

00002150: 012F  Return (NumLocal[0001])
00002157: 00B8  END OF FUNCTION ()

因此這裡最簡單的想法是把00002108處的不等於改成等於,正巧DIS有這個功能,右鍵點選它選Change to,改成等於就行了。退出DIS的時候會提示是否重新校驗INS檔案,因為SETUP在執行時會首先檢測INS檔案的CRC校驗和,因此這裡修改後也要重新校驗,選“是”後新的INS檔案就產生了。
再執行NetScanTools Pro 2000的安裝程式,序列號亂填一氣,安裝透過。

按理說通常的安裝軟體,破到這個程度也就差不多了,但NetScanTools Pro 2000多做了一點小手腳。本來我還挺高興地執行NSTPRO.EXE想看看自己的成果,NSTPRO.EXE卻忽地彈出個視窗說是“Invalid Installation……”並且要求重新安。?哇!看來NSTPRO.EXE啟動時還有一步序列號的判斷過程。序列號資訊存放在什麼地方呢?我連REGMON和FILEMON都懶得用了,直接開啟登錄檔尋尋覓覓就找到了這一處:HKEY_LOCAL_MACHINE\Software\Northwest Performance Software, Inc.\NetScanTools Pro\2000\NETSCANTOOLSPRO,下面是輸入的使用者名稱和序列號等。

首先就覺得似乎從NSTPRO.EXE中分析序列號演算法還不如從INS檔案中分析來得簡單,既然指令碼中判斷序列號是否正確的子程式在LABEL_004D處,就把它的內容列出來看看啦:

<LABEL_004D> REF: 00002108
  |
00002A92: 00B6  START OF FUNCTION (3*StrLocals + 10*NumLocals)
00002AA4: 0021  NumLocal[0005] = 00000081
//一個固定值
00002ABB: 0128  IF (StrLength (StrVar[001E]) < 00000018) THEN
00002ADB: 012F        Return (00000000)
00002ADC: 0000  ENDIF
//如果序列號長度小於18H則出錯
00002AE8: 0128  IF (NumLocal[0006] > 00000019) THEN
00002B08: 012F        Return (00000000)
00002B09: 0000  ENDIF
//長度大於19H也出錯,因此序列號只能是18H位或19H位。

00002B15: 0128  IF (NumLocal[0006] = 00000018) THEN
//如果長度是18H,則……
00002B35: 0021        NumLocal[0003] = 00000000
//N3是SL3的字串位置指標
00002B3F: 0021        NumLocal[0002] = 00000013
//N2是序列號的字串位置指標

<LABEL_0050> REF: 00002B9D
  |
00002B4D: 0128        IF (NumLocal[0002] <= 00000017) THEN
00002B6D: 007A            NumLocal[0009] = GetByte (StrVar[001E],NumLocal[0002])
//SV1E是使用者輸入的序列號
00002B78: 007B            SetByte (StrLocal[0003],NumLocal[0003],NumLocal[0009])
00002B83: 0119            NumLocal[0003] = NumLocal[0003] + 00000001
00002B90: 0119            NumLocal[0002] = NumLocal[0002] + 00000001
00002B9D: 002C            Goto (LABEL_0050)
00002B9E: 0000        ENDIF
//將SL3的值賦成序列號中的第13H位到17H位子串,該子串是數字。
00002BA6: 0021        NumLocal[0006] = 00000013
//並且N6記錄了該數字字串在序列號中出現的位置
00002BA7: 0000  ENDIF

//如果長度是19,則……
00002BB4: 0128  IF (NumLocal[0006] = 00000019) THEN
00002BD4: 0021        NumLocal[0003] = 00000000
00002BDE: 0021        NumLocal[0002] = 00000014

<LABEL_0053> REF: 00002C3C
  |
00002BEC: 0128        IF (NumLocal[0002] <= 00000018) THEN
00002C0C: 007A            NumLocal[0009] = GetByte (StrVar[001E],NumLocal[0002])
00002C17: 007B            SetByte (StrLocal[0003],NumLocal[0003],NumLocal[0009])
00002C22: 0119            NumLocal[0003] = NumLocal[0003] + 00000001
00002C2F: 0119            NumLocal[0002] = NumLocal[0002] + 00000001
00002C3C: 002C            Goto (LABEL_0053)
00002C3D: 0000        ENDIF
00002C45: 0021        NumLocal[0006] = 00000014
00002C46: 0000  ENDIF
00002C53: 0021  NumLocal[0002] = 00000000
//如果長度是19H,則取14H到18H位到SL3中。

<LABEL_0056> REF: 00002D5D
  |
//到這裡為止,N2是下一步即將進行的處理中的字串指標
//下一步是個大迴圈。

00002C61: 007A  NumLocal[0009] = GetByte (StrVar[001E],NumLocal[0002])

//N9是輸入序列號的某個字元

00002C6C: 011D  NumLocal[0007] = 000000FF & NumLocal[0009]

//N7=N9 AND 0xFF,個人感覺N7實際上等於N9


00002C79: 0119  NumLocal[0002] = NumLocal[0002] + 00000001
00002C86: 0021  NumLocal[0003] = 00000000

//N3是迴圈變數,此處初始化

<LABEL_0057> REF: 00002D35
  |
//以下是個小迴圈,處理序列號中的一個字元

00002C94: 0128  IF (NumLocal[0003] <= 00000007) THEN
00002CB4: 011D        NumLocal[0009] = NumLocal[0005] & 00000001
00002CC1: 011D        NumLocal[000A] = NumLocal[0007] & 00000001
00002CD9: 0022        IF (NumLocal[0009] ^ NumLocal[000A] != 00000000) THEN
00002CE7: 0122            NumLocal[0009] = NumLocal[0005] >> 00000001
00002CF4: 011F            NumLocal[0005] = NumLocal[0009] ^ 00008408
00002D01: 0000        ELSE
00002D0A: 0122            NumLocal[0005] = NumLocal[0005] >> 00000001
00002D0B: 0000        ENDIF
00002D1B: 0122        NumLocal[0007] = NumLocal[0007] >> 00000001
00002D28: 0119        NumLocal[0003] = NumLocal[0003] + 00000001
00002D35: 002C        Goto (LABEL_0057)
00002D36: 0000  ENDIF
//小迴圈結束

00002D5D: 0022  IF (NumLocal[0006] -  = 00000000) THEN GOTO LABEL_0056

//減一後等於0就跳回去繼續迴圈?不等呢就結束迴圈?這是疑點之一。

//大迴圈結束後開始驗證

00002D6B: 0120  NumLocal[0007] = NumLocal[0005] ~ NumLocal[0005]
00002D7E: 0121  NumLocal[0009] = NumLocal[0005] << 00000008
00002D8B: 0122  NumLocal[000A] = NumLocal[0007] >> 00000008
00002D98: 011D  NumLocal[000A] = NumLocal[000A] & 000000FF
00002DA5: 011E  NumLocal[0005] = NumLocal[0009] | NumLocal[000A]
00002DB0: 011D  NumLocal[0005] = NumLocal[0005] & 0000FFFF

//最後的結果是N5,與SL3的數字值比較,等則表示輸入的序列號合法。

00002DCD: 0128  IF (StrToNum (NumLocal[0004],StrLocal[0003]) < 00000000) THEN
00002DED: 012F        Return (FFFFFFFF)
00002DEE: 0000  ENDIF
00002DFA: 0128  IF (NumLocal[0005] != NumLocal[0004]) THEN
00002E18: 012F        Return (FFFFFFFF)
00002E21: 0000  ELSE
00002E2A: 012F        Return (00000001)
00002E2B: 0000  ENDIF
00002E37: 00B8  END OF FUNCTION ()

上邊我能看懂的地方加了一些註釋,名為STRVAL的都是字串型全域性變數,名裡有local的都是區域性變數。StrVar[001E]是我們輸入的序列號。這個程式首先判斷序列號是否是18H或19H長,然後取出序列號的最後五個數字。程式末尾把運算的結果與這個五位數字值比較,不等則出錯。整個運算過程雖不長,卻有點不符合常規。――我手頭沒有詳細介紹INSTALLshield指令碼語法的資料,上次皮特陳兄雖做了一個,卻不敢看(老當機),只有猜測著來。與或非左右移等符號和C中差不多,IFWHILEENDIF等和VB又差不多。我按照我的理解編了一段C程式碼來模仿這段運算過程。先輸入19個字元(0到0x13),再把這些字元運算得到五位數字(0x13到0x17),但拼湊起來的序列號根本通不過。我一直以為是我的指令碼演算法理解錯誤,山窮水盡了好幾天也沒什麼結果(對自己的水平沒有自信啦)。
    我也打算去找一個正確的序列號來驗證自己的想法,可這種軟體的這個版本的序列號在什麼啊嘶嗒啦唯嘶嗒上是找不到的。再後來,“我本善良”兄給了我一個能用的序列號9351324-005376-001-32602,確實後五位是數字,前19位應該是輸入的東西,只是前19位看起來有了許多限制,比如哪幾位是數字,哪一位是“-”等,這在指令碼里卻沒找到。
    現在只有從NSTPRO.EXE入手分析讀登錄檔中序列號的一段了。老法子,彈出出錯框時切入TRW查輸入的序列號再下Bpm記憶體斷點,偏偏TRW不穩定,記憶體斷點又斷不了,我氣得扔下它不幹了。過了幾天,該忙的似乎應付了一點後,弄了個SOFTICE來,這下子穩定多了。――由於這段時間忙,搞點破解只能擠出時間來,經常是研究幾個小時又停幾天,這就叫“三天破解,兩天曬網”,這種不良的風氣值得批鬥。
    後來透過幾個BPM斷點來到了這一段:
   
* Reference To: KERNEL32.lstrlenA, Ord:02A1h
                                  |
:00438D10 FF15C0D24900            Call dword ptr [0049D2C0]
:00438D16 83F818                  cmp eax, 00000018
:00438D19 0F858D000000            jne 00438DAC
:00438D1F 8D8C2494000000          lea ecx, dword ptr [esp+00000094]

* Possible Reference to String Resource ID=00019: "Edit the list of IP addresses and Hostnames."
                                  |
:00438D26 6A13                    push 00000013
:00438D28 51                      push ecx
:00438D29 E8C2210000              call 0043AEF0
:00438D2E 83C408                  add esp, 00000008
:00438D31 8D942494000000          lea edx, dword ptr [esp+00000094]
:00438D38 8D4C2418                lea ecx, dword ptr [esp+18]
:00438D3C 8BF0                    mov esi, eax
:00438D3E 52                      push edx
:00438D3F E848AB0400              call 0048388C
:00438D44 8D44241C                lea eax, dword ptr [esp+1C]

* Possible Reference to String Resource ID=00005: "Error getting host address:
%s"
                                  |
:00438D48 6A05                    push 00000005
:00438D4A 50                      push eax
:00438D4B 8D4C2420                lea ecx, dword ptr [esp+20]
:00438D4F C684245C63000001        mov byte ptr [esp+0000635C], 01
:00438D57 E8E3440400              call 0047D23F                //取序列號後五位子串
:00438D5C 50                      push eax
:00438D5D 8D4C241C                lea ecx, dword ptr [esp+1C]
:00438D61 C684245863000002        mov byte ptr [esp+00006358], 02
:00438D69 E8A9AB0400              call 00483917
:00438D6E 8D4C241C                lea ecx, dword ptr [esp+1C]
:00438D72 C684245463000001        mov byte ptr [esp+00006354], 01
:00438D7A E89FAA0400              call 0048381E
:00438D7F 8B4C2418                mov ecx, dword ptr [esp+18]
:00438D83 51                      push ecx
:00438D84 E8572F0300              call 0046BCE0
:00438D89 81E6FFFF0000            and esi, 0000FFFF
:00438D8F 83C404                  add esp, 00000004
:00438D92 3BF0                    cmp esi, eax            

//此處EAX和ESI,一個是根據前多少位計算出來的校驗值,一個是序列號中後五位轉換成的數字值,所以這裡是一步比較關鍵的跳轉。

:00438D94 7502                    jne 00438D98
:00438D96 33FF                    xor edi, edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00438D94(C)
|
:00438D98 8D4C2418                lea ecx, dword ptr [esp+18]
:00438D9C C684245463000000        mov byte ptr [esp+00006354], 00
:00438DA4 E875AA0400              call 0048381E
:00438DA9 83CEFF                  or esi, FFFFFFFF

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00438D19(C)
|
:00438DAC 8D942494000000          lea edx, dword ptr [esp+00000094]
:00438DB3 52                      push edx

* Reference To: KERNEL32.lstrlenA, Ord:02A1h
                                  |
:00438DB4 FF15C0D24900            Call dword ptr [0049D2C0]
:00438DBA 83F819                  cmp eax, 00000019
:00438DBD 0F858D000000            jne 00438E50
:00438DC3 8D842494000000          lea eax, dword ptr [esp+00000094]

* Possible Reference to String Resource ID=00020: "If checked, get latest news from our website when first view"
                                  |
:00438DCA 6A14                    push 00000014
:00438DCC 50                      push eax
:00438DCD E81E210000              call 0043AEF0
:00438DD2 83C408                  add esp, 00000008
:00438DD5 8D8C2494000000          lea ecx, dword ptr [esp+00000094]
:00438DDC 8BF0                    mov esi, eax
:00438DDE 51                      push ecx
:00438DDF 8D4C241C                lea ecx, dword ptr [esp+1C]
:00438DE3 E8A4AA0400              call 0048388C
:00438DE8 8D54241C                lea edx, dword ptr [esp+1C]

* Possible Reference to String Resource ID=00005: "Error getting host address:
%s"
                                  |
:00438DEC 6A05                    push 00000005
:00438DEE 52                      push edx
:00438DEF 8D4C2420                lea ecx, dword ptr [esp+20]
:00438DF3 C684245C63000003        mov byte ptr [esp+0000635C], 03
:00438DFB E83F440400              call 0047D23F
:00438E00 50                      push eax
:00438E01 8D4C241C                lea ecx, dword ptr [esp+1C]
:00438E05 C684245863000004        mov byte ptr [esp+00006358], 04
:00438E0D E805AB0400              call 00483917
:00438E12 8D4C241C                lea ecx, dword ptr [esp+1C]
:00438E16 C684245463000003        mov byte ptr [esp+00006354], 03
:00438E1E E8FBA90400              call 0048381E
:00438E23 8B442418                mov eax, dword ptr [esp+18]
:00438E27 50                      push eax
:00438E28 E8B32E0300              call 0046BCE0
:00438E2D 81E6FFFF0000            and esi, 0000FFFF
:00438E33 83C404                  add esp, 00000004
:00438E36 3BF0                    cmp esi, eax

//這裡與上面的是一樣的,所不同的應該就是一個處理0X18位,一個處理0X19位。

:00438E38 7502                    jne 00438E3C
:00438E3A 33FF                    xor edi, edi   

    如果要找註冊碼運算的地方,可從前面跟進去到下面的這個地方(如果是0X18位序列號的話):

                                  |
:00445DF7 6A13                    push 00000013
:00445DF9 50                      push eax
:00445DFA E8F150FFFF              call 0043AEF0            //從這裡再跟進去。
:00445DFF 83C408                  add esp, 00000008
:00445E02 8D4C241C                lea ecx, dword ptr [esp+1C]
:00445E06 8BF8                    mov edi, eax
:00445E08 51                      push ecx
:00445E09 8D4C2414                lea ecx, dword ptr [esp+14]
:00445E0D E87ADA0300              call 0048388C
:00445E12 8D542414                lea edx, dword ptr [esp+14]

    從00445DFA處跟進CALL中,程式碼如下:
   
:0043AEF0 55                      push ebp
:0043AEF1 8B6C240C                mov ebp, dword ptr [esp+0C]

//BP迴圈控制,目前是13,似乎對應著00002BA6: 0021 的NumLocal[0006] = 00000013

:0043AEF5 6685ED                  test bp, bp

* Possible Reference to Dialog: DialogID_0081
                                  |
:0043AEF8 B881000000              mov eax, 00000081

//這個0X81的常數是否有些眼熟?――就是INSTALLSHIELD反編譯指令碼中的
00002AA4: 0021  NumLocal[0005] = 00000081 哇!

:0043AEFD 7506                    jne 0043AF05
:0043AEFF 66B87EFF                mov ax, FF7E
:0043AF03 5D                      pop ebp
:0043AF04 C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043AEFD(C)
|
:0043AF05 57                      push edi
:0043AF06 8B7C240C                mov edi, dword ptr [esp+0C]
:0043AF0A 56                      push esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043AF37(C)
|
:0043AF0B 8A0F                    mov cl, byte ptr [edi]        //讀一個字元

* Possible Reference to String Resource ID=00008: "Error connecting to host:
%s"
                                  |
:0043AF0D BE08000000              mov esi, 00000008            //對每一個字元要進行八次迴圈。

//

:0043AF12 81E1FF000000            and ecx, 000000FF

//對應著

:00

相關文章