• 标 题:SoftIce45_for_win9x的安装序列码获取及序列码生成机编写(无实际意义,纯粹好玩) (9千字)
  • 作 者:Leafred
  • 时 间:2001-5-23 8:34:55
  • 链 接:http://bbs.pediy.com

标题:SoftIce45_for_win9x的安装序列码获取及序列码生成机编写
作者:leafred
主页:http://leafred.126.com/
工具:isDcc v1.22,TRW2000

如有错误欢迎来信指正(leafspring@21cn.com)

以前搞破解,都用TRW2000,今天为了搞一个程序,结果TRW2000总是出错,无奈想起SoftIce,高高兴兴的下载完SoftIce,准备安装,没想到是要序列码的,谁叫咱会Crack呢,自己来动手找吧。哈哈,来破解Crack工具,有点滑稽吧。

先用isDcc来把setup.ins给反编译出来,当然了,setup.ins是在SoftIce安装时在系统临时目录下找到的了。包括后面要提到的动态库。
首先是想能直接从反编译的文件总给找到,来个“The serial number you entered is not valid for this product”搜索,铛的一声,弹出一个提示Search string not found!,心一沉,看来要费点劲了。
慢慢看吧。
嗯,这个声明prototype UTILITY.DigitCheck(string);可疑,从字面上看数字检查,很想序列码检查哎,先看看谁调用它吧。
找到一下这段代码,发现是function143调用,再找function143谁调用,发现果真是此处进行序列码检查。

首先看看调用function143的地方:
abel187: //Ref: 00667F 
006699:0152:        RegDBGetKeyValueEx(lString0, "Onomatopoeia", lNumber1, lString1, lNumber3);
====》取得序列码,先从注册表获取初始值,让我们进行修改,原来没有当然输入新值了。

。。。。。。

label188: //Ref: 0066D0 
====》先进行数据初步校验
0066FF:00B4:        NMINST32.ValidCookie(2, 0, lString1);
006711:0021:        lNumber5 = LAST_RESULT;
006719:0128:        lNumber5 = lNumber5 = 1;
00672B:0022:        if (lNumber5 = 0) then
                        goto label190;
                    endif;
=====》初步校验通过,进行正式检查                   
006739:00B5:        function143(lString3);

。。。。。。


现在再来看函数
  // ------------- FUNCTION function143 --------------
    function function143(pString0)
====》省略掉变量声明
。。。。。

    begin
0067BE:002F:        StrLength(pString0); ==》获取长度
0067C3:0021:        lNumber1 = LAST_RESULT;
0067CB:0128:        lNumber1 = lNumber1 != 14;
=====》长度不等于14 ?
0067DD:0022:        if (lNumber1 = 0) then
=======》 No,等于14,OK下一步检查
                        goto label192;
                    endif;
                    ====》Yes,不等于14,返回0,这里可以知道输入的字符共12位,因为还含两个“-”
0067EB:012F:        return(0);

label192: //Ref: 0067DD 
=====》取第一个框里的字符
0067F8:0030:        StrSub(lString1, pString0, 0, 4);
=====》取第二个框里的字符
00680A:0030:        StrSub(lString2, pString0, 5, 6);
=====》取第三个框里的字符
00681C:0030:        StrSub(lString3, pString0, 12, 2);
00682E:0124:        lString6 = lString1 + lString2;
006839:0124:        lString0 = lString6 + lString3;
====》以上代码合并输入的内容,去掉“-”
006844:0031:        StrFind(lString0, "-");
=====》还含有“-”吗?
00684D:0128:        lNumber1 = LAST_RESULT >= 0;
00685F:0022:        if (lNumber1 = 0) then
====》No,不含了,OK继续检查
                        goto label193;
                    endif;
                    ====》还含“-”,返回0,由此看出我们输入的字符不可含有“-”
00686D:012F:        return(0);

====》现在开始装载UTILITY.dll了
label193: //Ref: 00685F 
00687A:0125:        lString5 = SUPPORTDIR ^ "UTILITY.dll";
006890:00B2:        UseDLL(lString5);
006895:0021:        lNumber1 = LAST_RESULT;
00689D:0128:        lNumber1 = lNumber1 = 0;
0068AF:0022:        if (lNumber1 = 0) then
                        goto label194;
                    endif;
======》调用UTILITY.dll的DigitCheck来检查
0068BD:00B4:        UTILITY.DigitCheck(lString0);
0068C5:0021:        lNumber0 = LAST_RESULT;
0068CD:00B3:        UnUseDLL(lString5);

label194: //Ref: 0068AF 
0068D6:0128:        lNumber1 = lNumber0 = 1;
0068E8:0022:        if (lNumber1 = 0) then
                        goto label195;
                    endif;
0068F6:012F:        return(1);
0068FF:002C:        goto label196;

OK,至此我们已经知道它如何进行序列号检查的,首先进行ValidCookie,通过则检查长度,长度也通过进行最后的DigitCheck。
我们现在可以把上面用来检查的两个动态库搞过来反汇编,慢慢读程序,我现在就不这样做了,我直接用TRW2000了,不要installshiled防TRW2000哦。

老一套了,用hmemcpy中断,几个F12来到此处:
017F:01541C44  LEA      EAX,[ESP+0C]
017F:01541C48  PUSH    EAX
017F:01541C49  PUSH    DWORD 01555820
017F:01541C4E  CALL    0154AAA0
在这里我们来个D EAX看看,发现是第一个框里的前三位数字,其实往上看看我们就可以看到前三个数字被移到ESP+0C处的代码。它是先移到ESP+10处,然后把ESP减4,POP一个值了。不就是用ESP+0C来引用吗。
再D 01555820,发现是许多数字组合,再跟进去,发现0154AAA0是用来判断EAX的内容是不是在01555820中的某个数字组合中。
下面还有几处,只要有一处找到,就可以了。
因此可以知道前三个数字必须在这些数字组合中,具体的是:
230 400 401 410 411 420 421 430 431 480 481 510 930 931 950 951 ==》第一处
231 401 411 421 431 481 511 931 951 ===》第二处,一下还有几处全部没有,也就是说它现在只使用两处,下面的几处可能是保留给将来版本使用。

好了,现在再随便挑一个,重新输入再进入,(当然也可以直接改了)
来到此处:
017F:01541CA1  CALL    0154A5F0
关键所在,F8进入。
017F:0154A64E  PUSH    DWORD 01556C20
017F:0154A653  CALL    `KERNEL32!LoadLibraryA`
017F:0154A659  MOV      ESI,EAX
017F:0154A65B  TEST    ESI,ESI
017F:0154A65D  JNZ      0154A693
====》装载UTILITY.dll
。。。。。

017F:0154A693  PUSH    DWORD 01554588
017F:0154A698  PUSH    ESI
017F:0154A699  CALL    `KERNEL32!GetProcAddress`==》获取DigitCheck入口
017F:0154A69F  TEST    EAX,EAX
017F:0154A6A1  JZ      0154A6B1
017F:0154A6A3  LEA      ECX,[ESP+0C]
017F:0154A6A7  PUSH    ECX
017F:0154A6A8  CALL    EAX ======》此处进入DigitCheck了!!!!!
017F:0154A6AA  ADD      ESP,BYTE +04

进入017F:0154A6A8  CALL    EAX
。。。。。。

017F:01BC1136  CALL    01BC11D0
=====》此处功能是查表求得8个数字(包含对非数字的特殊处理)
017F:01BC113B  CALL    01BC1280
=====》利用上面的8个数字,再查表得8个数字。
。。。。。。

=====》01BC91B0处存放的是上面求得的8个数字
017F:01BC114A  MOV      AL,[ECX+01BC91B4]===》后四位
017F:01BC1150  MOV      DL,[ECX+01BC91B0]===》前四位
017F:01BC1156  XOR      AL,DL ===》先XOR ECX+4与ECX处数字(ECX从0到3)
017F:01BC1158  MOV      DL,[01BC91B7] ===》最后一位
017F:01BC115E  OR      AL,DL ====》 再OR最后一位
017F:01BC1160  OR      AL,30===》再OR 0x30
017F:01BC1162  CMP      AL,39 ===》判断是否为数字
017F:01BC1164  MOV      [ECX+01BC91B8],AL
017F:01BC116A  JNG      01BC1174
017F:01BC116C  ADD      AL,07 ===》 加7转为字母(A-Z)
017F:01BC116E  MOV      [ECX+01BC91B8],AL
017F:01BC1174  MOV      AL,[ECX+01BC91B8]
017F:01BC117A  MOV      DL,[ECX+01BC9188]
017F:01BC1180  CMP      AL,DL ====》与输入的后四位比较
017F:01BC1182  JZ      01BC118A
017F:01BC1184  OR      AL,20 ===》不分大小写
017F:01BC1186  CMP      AL,DL
017F:01BC1188  JNZ      01BC119C
017F:01BC118A  INC      ECX
017F:01BC118B  CMP      ECX,BYTE +04
017F:01BC118E  JL      01BC114A
017F:01BC1190  MOV      [01BC91AC],ECX
017F:01BC1196  MOV      EAX,01
017F:01BC119B  RET   
至此,我们完全清楚它干什么了,我们可以在017F:01BC1180 中断。
每次修改DL让它与AL相等,并几下AL值四次AL的值就是正确的后四位。
也就是说它根据我们输入的东西,判一下前三位,然后进行计算,算出后四位,再与输入的后四位比较,正确,则通过。

因而,也就有了序列码生成机(不知可有更好的名字,因为它不能叫做注册机了)的概念。
代码如下:(仅仅要你输入前八位数值,不带“-”)

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    unsigned int tab[8][10] = {
        {15,1,11,3,8,4,13,7,12,0},
        {10,12,1,8,2, 0 ,9, 15, 5, 11},
        {9 ,5, 12, 2, 7, 6, 15, 4 , 14, 10},
        {3, 4, 12, 11,1, 0, 13 ,8, 0, 14},
        {13 ,1, 6,11 ,8 ,10 ,14 ,4,3, 12},
        {7,11,6,10,5,9,4,8,0,3},
        {0,12,3,15,10,8,2,12,4,6},
        {9, 5, 13, 1,3,11,12 ,4, 2 ,8}
} ; //索引表
    char  *fthree[] ={
        "230" ,"231","400","401","410","411" ,"420", "421", "430", "431",
        "480", "481","510","511","930","931", "950","951"
    }; //前三个字符范围
    unsigned char fthreelen = 18;
    char regcode[4],getcode[80];
    int i,j;
    unsigned int itmp;
    unsigned int code[8] ={0,0,0,0,0,0,0,0} ;

    printf("SoftIce4.5 for Win9x serial number Generater\n");
    printf("Written by Leafred\n");
    printf("Http://leafred.126.com/\n");
    printf("\nPlease Input the initial 8 letters:\n");
    do {
    gets(getcode);
    if(strlen(getcode) != 8)
        printf("Please Input the initial 8 letters:\n");
    else
        break;

    } while(1) ;
    for(i=0 ;i < fthreelen ; i++)
        if(! strncmp(getcode,fthree[i],3)) break;
    if(! (i < fthreelen)){
        printf("\nFirst three letters must in:\n");
        for(i=0;i< fthreelen;i++)
            printf("%s ",fthree[i]);
        printf("\n");
        return 0 ;
    }
    strupr(getcode);
    //第一个CALL
    for(i=0;i<8;i++){
        itmp = getcode[i] ;
        if(itmp >='0' && itmp <= '9')
            itmp = 132;
        else if(itmp >= 'A' && itmp <= 'F')
            itmp = 129;
        else if(itmp >= 'G' && itmp <= 'Z')
            itmp = 1;
        else if(itmp >= 'a' && itmp <= 'f')
            itmp = 130;
        else if(itmp >= 'g' && itmp <= 'z')
            itmp = 2;
        else
            itmp = 16;
        if(!(itmp & 4))break;
        code[i] = getcode[i] & 0xf;
    }
    //对前8各输入有字母处理
    if(i < 8)
    {
        i--;
        j = 7;
        do{
            if(i>=0){
                code[j]=code[i];
                i--;
            }
            else
                code[j] = 0;
            j--;
            if(j < 0)
                break;

        }while (1) ;
    }
    //查表取值
    for(i=0;i<8;i++)
        code[i] = tab[i][code[i]];
        
    //最后一步
    for(i=0;i<4;i++){
        regcode[i] = code[i] ^ code[i+4];
        regcode[i] |= code[7];
        regcode[i] |= 0x30 ;
        if(regcode[i] > 0x39)regcode[i] += 7;
    }
    printf("Your serial number is:\n");
    printf("%c%c%c%c-%s%c%c-%c%c\n",getcode[0],getcode[1],getcode[2],getcode[3],getcode+4,regcode[0],regcode[1],regcode[2],regcode[3]);
    return 0;
}