SoftICE4.05 for win9x的安装序列号破解(2) (11千字)
SoftICE4.05 for win9x的安装序列号破解(2)Passion(接上半部分)
跟踪过程:
Exported fn(): DigitCheck - Ord:0001h
:10001110 56 push esi
:10001111 57 push edi
:10001112 8B7C240C mov edi, dword ptr [esp+0C] //当然是序列号的地址了。
:10001116 83C9FF or ecx, FFFFFFFF
:10001119 33C0 xor eax, eax
:1000111B F2 repnz
:1000111C AE scasb //先求长度。
:1000111D F7D1 not ecx
:1000111F 2BF9 sub edi, ecx
:10001121 8BC1 mov eax, ecx
:10001123 8BF7 mov esi, edi
:10001125 BF80910010 mov edi, 10009180
:1000112A C1E902 shr ecx, 02
:1000112D F3 repz
:1000112E A5 movsd //再搬地方。
:1000112F 8BC8 mov ecx, eax
:10001131 83E103 and ecx, 00000003
:10001134 F3 repz
:10001135 A4 movsb //怕没搬完,再搬零头。
:10001136 E895000000 call 100011D0
:1000113B E840010000 call 10001280
//这两个CALL会根据序列号的前8位数字经过查表运算得来一系列值,也就内定了前八个字符必须是数字。详细内容见后面。
计算出来的八个值:04 0C 09 03 0D 07 06 08,放在100091B0
:10001140 33C9 xor ecx, ecx
:10001142 5F pop edi
:10001143 890DAC910010 mov dword ptr [100091AC], ecx
:10001149 5E pop esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000118E(C)
|
:1000114A 8A81B4910010 mov al, byte ptr [ecx+100091B4] //取第一个
:10001150 8A91B0910010 mov dl, byte ptr [ecx+100091B0] //取第四个
:10001156 32C2 xor al, dl //先异或
//八个值中,第一个值和第四个值异或,再和最后一个值或,结果如不是数字或者字母则加7变成字母,比较后通过则继续计算第二个和第五个,并且仍和最后一个或,依此类推。
:10001158 8A15B7910010 mov dl, byte ptr [100091B7] //取最后一个
:1000115E 0AC2 or al, dl //再或
:10001160 0C30 or al, 30
:10001162 3C39 cmp al, 39
:10001164 8881B8910010 mov byte ptr [ecx+100091B8], al //存结果
:1000116A 7E08 jle 10001174
:1000116C 0407 add al, 07 //变字母
:1000116E 8881B8910010 mov byte ptr [ecx+100091B8], al //也是存结果
//这里的AL是算出来的一个字符,算出一个后马上跟输入的序列号的最后四位比较。
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000116A(C)
|
:10001174 8A81B8910010 mov al, byte ptr [ecx+100091B8]
:1000117A 8A9188910010 mov dl, byte ptr [ecx+10009188]
//这是输入序列号的后四位地址。
:10001180 3AC2 cmp al, dl
//前八位数字计算出来的四个字符必须和序列号末尾的四个字母一样才行。
:10001182 7406 je 1000118A
//比较一个字符,这里得修改程序流程,让它继续跳回去计算正确的后几个字母。
:10001184 0C20 or al, 20
:10001186 3AC2 cmp al, dl
:10001188 7512 jne 1000119C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001182(C)
|
:1000118A 41 inc ecx
:1000118B 83F904 cmp ecx, 00000004 //比较四次也就是四个字符。
:1000118E 7CBA jl 1000114A
:10001190 890DAC910010 mov dword ptr [100091AC], ecx
:10001196 B801000000 mov eax, 00000001 //比较通过就返回1。
:1000119B C3 ret
程序算出来的四个字符是“9BFB”
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001188(C)
|
:1000119C 890DAC910010 mov dword ptr [100091AC], ecx
:100011A2 33C0 xor eax, eax //否则返回0。
:100011A4 C3 ret
这里是第一个CALL的内容,它的功能也就是先根据输入的前8个字符查一张表,如果查出来的东西符合要求(也就是前8个字符都是数字)则把前8个字符的ASCII码都转换成实际的数字存放在100091B0处:
* Referenced by a CALL at Address:
|:10001136
|
:100011D0 33D2 xor edx, edx
:100011D2 56 push esi
:100011D3 8915A8910010 mov dword ptr [100091A8], edx
:100011D9 33C9 xor ecx, ecx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000123D(U)
|
:100011DB A16C830010 mov eax, dword ptr [1000836C]
:100011E0 890DAC910010 mov dword ptr [100091AC], ecx
:100011E6 83F801 cmp eax, 00000001
:100011E9 7E20 jle 1000120B
//也不知是什么判断,反正总是跳。
:100011EB 0FBE8180910010 movsx eax, byte ptr [ecx+10009180]
:100011F2 6A04 push 00000004
:100011F4 50 push eax
:100011F5 E8C4070000 call 100019BE
:100011FA 8B15A8910010 mov edx, dword ptr [100091A8]
:10001200 8B0DAC910010 mov ecx, dword ptr [100091AC]
:10001206 83C408 add esp, 00000008
:10001209 EB13 jmp 1000121E
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100011E9(C)
|
:1000120B 0FBE8180910010 movsx eax, byte ptr [ecx+10009180] //表首址
//这里是第一张表
* Possible StringData Ref from Data Obj ->" ((((( "
->" H剟剟剟剟剟亖亖亖"
->"倐倐倐"
->" "
|
:10001212 8B3560810010 mov esi, dword ptr [10008160]
:10001218 8A0446 mov al, byte ptr [esi+2*eax] //查表
:1000121B 83E004 and eax, 00000004
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001209(U)
|
:1000121E 85C0 test eax, eax
:10001220 741D je 1000123F //如果输入的不是数字,则这里会跳。
:10001222 83F908 cmp ecx, 00000008 //满了8个则跳。
:10001225 7D18 jge 1000123F
:10001227 8A8180910010 mov al, byte ptr [ecx+10009180]
:1000122D 240F and al, 0F
:1000122F 8882B0910010 mov byte ptr [edx+100091B0], al
:10001235 42 inc edx
:10001236 8915A8910010 mov dword ptr [100091A8], edx
:1000123C 41 inc ecx
:1000123D EB9C jmp 100011DB
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:10001220(C), :10001225(C)
|
:1000123F 83FA08 cmp edx, 00000008
:10001242 5E pop esi
:10001243 7D34 jge 10001279
:10001245 4A dec edx
:10001246 B807000000 mov eax, 00000007
:1000124B 8915A8910010 mov dword ptr [100091A8], edx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000126C(C)
|
:10001251 85D2 test edx, edx
:10001253 7D09 jge 1000125E
:10001255 C680B091001000 mov byte ptr [eax+100091B0], 00
:1000125C EB0D jmp 1000126B
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001253(C)
|
:1000125E 8A8AB0910010 mov cl, byte ptr [edx+100091B0]
:10001264 4A dec edx
:10001265 8888B0910010 mov byte ptr [eax+100091B0], cl
//这里100091b0处执行完后就存放了前8位数字的实际值,也就是
05 01 00 00 00 00 09 09
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000125C(U)
|
:1000126B 48 dec eax
:1000126C 79E3 jns 10001251
:1000126E A3AC910010 mov dword ptr [100091AC], eax
:10001273 8915A8910010 mov dword ptr [100091A8], edx
//这里存放俩处理过的字节数,都是8。
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001243(C)
|
:10001279 C3 ret
下面是第二个CALL的内容,这个CALL把第一个CALL计算出来的数字再查一次表,结果覆盖了100091b0处的8个字节:
* Referenced by a CALL at Address:
|:1000113B
|
:10001280 33C0 xor eax, eax
:10001282 A3AC910010 mov dword ptr [100091AC], eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100012A7(C)
|
:10001287 0FBE88B0910010 movsx ecx, byte ptr [eax+100091B0]
:1000128E 8B148590800010 mov edx, dword ptr [4*eax+10008090] //又查一次表
:10001295 40 inc eax
:10001296 83F808 cmp eax, 00000008 //处理8个字符
:10001299 8A0C0A mov cl, byte ptr [edx+ecx]
:1000129C A3AC910010 mov dword ptr [100091AC], eax
:100012A1 8888AF910010 mov byte ptr [eax+100091AF], cl //存查表结果
:100012A7 7CDE jl 10001287
:100012A9 C3 ret //返回
查出来的结果仍然放在100091b0,这里变成了04 0C 09 03 0D 07 06 08
最后得可以用的序列号: 5100-000099-9BFB
需要说明的是,这一大段是检验从注册表里读出来的序列号,不是检验输入的。——其实都大同小异嘛,不信,在反编译的sice405.txt中再查找一下“function143”看看,还有这一处被调用了:
label185:
00664E:0152: RegDBGetKeyValueEx(lString0, "Serial", lNumber1, lString1, lNumber3);
006665:0021: lNumber4 = LAST_RESULT;
00666D:0128: lNumber5 = lNumber4 = 0;
00667F:0022: if (lNumber5 = 0) then
goto label187;
endif;
00668D:0013: lString3 = lString1;
label186:
006699:0152: RegDBGetKeyValueEx(lString0, "Onomatopoeia", lNumber1, lString1, lNumber3);
0066B6:0021: lNumber4 = LAST_RESULT;
0066BE:0128: lNumber5 = lNumber4 = 0;
0066D0:0022: if (lNumber5 = 0) then
goto label188;
endif;
0066DE:006D: StrToNum(lNumber0, lString1);
0066E6:0122: lNumber0 = lNumber0 >> 13;
0066F3:006E: NumToStr(lString1, lNumber0);
//推测应该是序列号前8位转化成纯数字值再右移13位后成的数字必须能通过下面的这个函数检验。
label187:
0066FF:00B4: NMINST32.ValidCookie(2, 0, lString1);
//这是多出来的一重检验函数,但运行时nminst32.dll的这个输出函数无论如何都跟不进去(不断),也不知道是怎么回事。
006711:0021: lNumber5 = LAST_RESULT;
006719:0128: lNumber5 = lNumber5 = 1;
00672B:0022: if (lNumber5 = 0) then
goto label190;
endif;
006739:00B5: function143(lString3);
//这里就是检验输入序列号是否合法的,检验形式基本一样,只不过前面多了一重束缚。
006744:0021: lNumber5 = LAST_RESULT;
00674C:0128: lNumber5 = lNumber5 = 1;
00675E:0022: if (lNumber5 = 0) then
goto label189;
endif;
00676C:0013: string13 = lString3;
006774:012F: return(1);
00677D:002C: goto label190;
还有一点,前8位数字不是随便填什么都能在10001140处中断的,估计就是那个NMINST32.ValidCookie(2, 0, lString1);搞的鬼。我在跟踪过程中却不见程序执行到这个函数的入口,也不知道是什么原因。
另外SoftICE4.05是要找出能用的序列号才需要这么麻烦地跟,倘若只想用暴力跳过检验安装成功,只需来一个bpx messageboxA再看看上面第一个JNZ的指令就行,实在不难。
再写几个随便跟出来的、可以用的序列号(在已知某些可用的序列号的基础上,;P )
5107-0145BF-BB
5107-0045BB-BB
1107-0745FF-BF
其实这个破得也不算完美,只是时间紧,写得不好,大伙儿包涵包涵。——这还是皮特陈大客“点将”的“恶果”,有气儿找他去,我躲了。哈!
