• 标 题:破解NetScanTools Pro 2000及其InstallShield脚本破解(其实脚本没破成) (18千字)
  • 作 者:Passion
  • 时 间:2001-3-30 23:50:41
  • 链 接:http://bbs.pediy.com

破解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

  • 标 题:上面这篇是前几天的,今晚再贴个破破烂烂的序列机。 (2千字)
  • 作 者:Passion
  • 时 间:2001-3-30 23:53:55

今晚忙里偷闲写了个序列机,手头没VC,只好用DELPHI,其实也就是把那段代码套过来。下面是序列机的源程序:
   
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Mask;

type
  TForm1 = class(TForm)
    MaskEdit1: TMaskEdit;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  j:integer;
  inputstr:string;
  adr:Pointer;
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
label
L_0043AEF8,L_0043AF0B,L_0043AF19,L_0043AF27,L_0043AF0D;
begin
inputstr:=self.maskedit1.text;
adr:=Pchar(inputstr);
asm
                    push ebp
                    mov ebp, 13h
                    test bp, bp
L_0043AEF8:        mov eax, 00000081h
                    push edi
                    mov edi, adr
                    push esi
L_0043AF0B:        mov cl, byte ptr [edi]    //读一个字符
L_0043AF0D:        mov esi, 00000008h        //对每一个字符要进行八次循环。
                    and ecx, 000000FFh
                    inc edi

L_0043AF19:        mov edx, eax
                    xor edx, ecx
                    test dl, 01h
                    je L_0043AF27
                    xor eax, 00010810h

L_0043AF27:        shr eax, 1
                    shr ecx, 1
                    dec esi            //小循环变量减1,要进行8次
                    jne L_0043AF19        //小循环
                    add ebp, 0000FFFFh        //大循环变量减1,要进行0x13次
                    test bp, bp
                    jne L_0043AF0B        //大循环
                    not eax
                    xor ecx, ecx
                    pop esi
                    mov cl, ah
                    pop edi
                    mov ch, al
                    pop ebp
                    mov eax, ecx
                    mov j, eax

end;
  self.Edit1.Text:=format('%s%5.5d',[inputstr,j]);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
messagebox(self.handle,'NetScanTools Pro 2000安装序列号生成器。'+#10+#13+'在框中输满所有数字即可生成序列号。'+#10+#13+#10+#13+'该破序列机作者:Passion','说明',MB_OK);
end;

end.