网际金典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时还有另外一串更复杂的检验算法,我下的不是完全安装版,没法子判断到底哪个才是正宗的,或者两个都是?——这就得靠皮特陈兄用正式安装版来检验检验了。^_^
- 标 题:皮兄,网际金典3的InstallShield序列号破解。 (9千字)
- 作 者:Passion
- 时 间:2001-4-28 23:27:06
- 链 接:http://bbs.pediy.com