Source Insight最新版免费可用

Source Insight注册算法研究

前言

挺火的一款国外的源码浏览编辑器,自己也有用所以顺便搞搞作为研究学习。

文末有惊喜!

分析

软件基本信息

截止目前我下载到的最新版:4.0

主程序MD5:B616825EF3AACB686F3313028D479536

定位注册码校验关键函数

随便写一个注册码,不出意外我们会得到这么个提示

在这里插入图片描述

这里有两种比较快速的方法定位到关键函数,一个是给弹信息框相关的函数下断点,另一个是根据字符串定位。无论哪种方法,最后都能够定位到关键函数。

在函数断下后,查看调用栈,最终就能找到验证序列号的函数了。

序列号验证算法分析

函数地址:0x51B5F0

相关伪代码:

...
GetInputSerial_406680(a1, 30, serial);        // 取编辑框中用户输入的序列号,不超过30个字节
  sub_4482B0(serial);                           // 删除特定字符 \t ,' '
  _strupr(serial);                              // 转大写
  sub_4064A0(a1, 30, serial);                   // 更新输入框内容
  if ( !CheckSerial_5187F0(serial, (_DWORD *)ArgList + 386, (_DWORD *)ArgList + 387, (_DWORD *)ArgList + 385, 1) )
  {
    if ( sub_56CC70((int)serial) )
      PopMsgBox_40B460(
        "The serial number you entered is for version 3.x of Source Insight.\n"
        "\n"
        "This version requires a version 4.x serial number.",
        v8);
    else
      PopMsgBox_40B460(
        "The serial number you entered is not correct. A serial number for Source Insight version 4.x is required.\n"
        "\n"
        "Please check the number and re-enter it.",
        v8);
    sub_406CD0(a1, 30);
    sub_4047C0();
    ++dword_684E0C;
    if ( dword_684E0C >= sub_451500(1) + 3 )
    {
      dword_684E0C = 0;
      sub_44FFE0();
      Sleep(0x1B58u);
      sub_450050();
      return 0;
    }
    return 0;
  }
  dword_684E0C = 0;
  if ( *((_DWORD *)ArgList + 385) != HIBYTE(dword_662948) ) // HIBYTE(dword_662948) = 0,猜测应该是主程序的MinVer
  {
    PopMsgBox_40B460(
      "The serial number you entered is for a different version of Source Insight.\n"
      "\n"
      "This version requires a version 4.x serial number.",
      v8);
    sub_406CD0(a1, 30);
    sub_4047C0();
    return 0;
  }
  if ( *((_DWORD *)ArgList + 387) == 3 )
  {
    PopMsgBox_40B460("The serial number cannot be used with the 'release' version of Source Insight.", v8);
    sub_4047C0();
    return 0;
  }
  if ( !sub_518920(serial) ) // serial长度 == 19,且 serial[2] == 'U'
    return 0;
  if ( sub_51A190((char *)ArgList + 1284) )     // 读取注册表,检查是否存在旧版的序列号
  {                                             // 最终期望是到达这
    sub_413F30("Upgrade License was validated: %s");
  }
  else
  {
    PopMsgBox_40B460(
      "Sorry, but we could not validate your previous license. Please make sure you have the correct serial number for ve"
      "rsion 3.x.\n"
      "\n"
      "If your old version of Source Insight is running, you can see the serial number by selecting Help > About Source I"
      "nsight. Otherwise, the serial number was typically provided in an email activation message when you purchased the old license.",
      v8);
    sub_406CD0(a1, 30);
    sub_4047C0();
    sub_413E20("Upgrade License could not be validated", v7);
  }
  return 0;
}

重点分析 C h e c k S e r i a l _ 5187 F 0 \textcolor{cornflowerblue}{CheckSerial\_5187F0} CheckSerial_5187F0 s u b _ 51 A 190 \textcolor{cornflowerblue}{sub\_51A190} sub_51A190这两个函数。

CheckSerial_5187F0和sub__51A190的检查主要分为以下几个部分:

  • 检查serial的长度是否等于19个字节

  • serial是否为S4U*-*****-*****-*****形式

  • serial[3]只能是’R’或者’G’

  • serial[6]只能是’R’,‘G’,‘D’,‘F’

  • serial[15:18]转换成DWORD数值后与函数 s u b _ 517 F 20 \textcolor{cornflowerblue}{sub\_517F20} sub_517F20的返回结果相等。

    sub_517F20伪代码:

    int *__cdecl transform_517F20(_BYTE *str, unsigned int round, BYTE *key, int *ans)
    {
      unsigned int i; // esi
      BYTE v5; // cl
      unsigned int j; // eax
      int *result; // eax
    
      for ( i = 0; i < 4; *((_BYTE *)ans + i - 1) = byte_60CAE8[v5 % 26] )
      {
        v5 = key[(unsigned __int8)(i + *str)];
        for ( j = 1; j < round; ++j )
          v5 = key[v5 ^ (char)str[j]];
        result = ans;
        ++i;
      }
      return result;
    }
    

    这个函数作用是根据serial[0:14]算出serial[15:18]的内容,只要保证serial[0:14]是合法的,那么通过这个函数自然就得出serial[15:18]的内容,不需要对这个函数做过多分析,只需要模拟一遍即可。

s u b _ 51 A 190 \textcolor{cornflowerblue}{sub\_51A190} sub_51A190函数会让我们输入3.0的序列号,也就是上个版本的序列号。验证3.0序列号的关键函数为 C h e c k O l d V e r S e r i a l _ 56 C B F 0 \textcolor{cornflowerblue}{CheckOldVerSerial\_56CBF0} CheckOldVerSerial_56CBF0

BOOL __cdecl CheckOldVerSerial_56CBF0(char *serial)
{
  size_t prefix_len; // eax
  unsigned int v3; // eax
  int part3; // [esp+4h] [ebp-104h] BYREF
  char extDat[256]; // [esp+8h] [ebp-100h] BYREF

  prefix_len = strlen(aSi3us);
  if ( !MatchPrefix_4135C0((int)serial, (unsigned __int8 *)aSi3us, prefix_len)// 前缀:SI3US
    || !SplitAndExtract_56CB60(serial, part2, &part3)// 将serial按-分组,取第二个-分组部分
    || IsContinuous_4498F0(part2) )            // 判断第二个分组字母数字的排列是否连续,期望是不连续。
  {
    return 0;
  }
  v3 = CalcSum_56CB00(part2);                  // 根据第二分组算出第三分组
  return part3 == v3;
}

3.0序列号的格式:SI3US-XXXX-XXXX,要求第二个分组之间的字母数字排列不能是以字典序顺序连续的,并且第三分组转为DWORD数值后要与 C a l c S u m _ 56 C B 00 ( p a r t 2 ) \textcolor{orange}{CalcSum\_56CB00(part2)} CalcSum_56CB00(part2)的结果相等。函数 C a l c S u m _ 56 C B 00 ( ) \textcolor{cornflowerblue}{CalcSum\_56CB00()} CalcSum_56CB00()不需要过多分析,只需要模拟一遍就能得到正确结果。

当上面这两个序列号都输入正确后就会要求你输入个人信息,然后进入网络验证。

网络验证分析

函数地址:0x51BF90

相关伪代码:

int __thiscall sub_51BF90(int this)
{
 ...
  memset(Str, 0, sizeof(Str));
  if ( !sub_518C10((CHAR *)(this + 2156)) )
  {
    sub_413E20("InitHostId: failed", v3);
    sub_414210(0, 0);
    sub_414260();
  }
  result = HttpSendSecretInfo_518C80((void *)this, v4, 0x1FA0, 0);
  if ( result == 0xC8 )
  {
    sub_413F30("License Activated");
    if ( NetDataToHexBytes_4272E0(v4, (int)Str, 0x2000u) && (int)strlen(Str) >= 8 )
    {
      RecordRegInfo_51AF90((BYTE *)this, &Str[8]);
      sub_519880((_DWORD *)this, *(_DWORD *)(this + 3536));// 写注册表HKEY_CURRENT_USER\Software\Source Dynamics\Source Insight\4.0\Data下的DofNV键
      return GenLicense_5184E0((LPCCH)(this + 1884), &Str[8]);// 网络数据写到文件*.lic
    }
    else
    {
      return 464;
    }
  }
  return result;
}

HttpSendSecretInfo_518C80函数会将用户填写的信息和系统信息以POST方式发送到https:\\sls.sourceinsight.com,并获取服务器返回的数据。相关代码:

 v10 = (void *)HttpOpen_457C40("Source Insight", 0, 0, 0, 0);
  hInternet = v10;
  if ( v10 )
  {
    v12 = (void *)HttpConnect_457DF0(v10, Str, v9, 0, 0, 3u, 0, 0);
    v13 = v12;
    if ( v12 )
    {
      v14 = HttpOpenRequest_457FC0(v12, "POST", lpMultiByteStr, 0, 0, 0, v8, 0);
      v15 = v14;
      if ( v14 )
      {
        HttpAddHeaders_4581F0(v14, "Content-Type: application/x-www-form-urlencoded", 0xFFFFFFFF, 0x20000000u);
        HttpAddHeaders_4581F0(v15, "Accept: text/plain", 0xFFFFFFFF, 0x20000000u);
        sprintf((char *const)v25, "Content-length: %d\n", v7);
        HttpAddHeaders_4581F0(v15, (char *)v25, 0xFFFFFFFF, 0x20000000u);
        if ( HttpSendRequestW(v15, 0, 0, lpOptional, v7) )
        {
          HttpQueryInfoW(v15, 0x20000013u, &Buffer, &dwBufferLength, 0);
          if ( Buffer == 200 )
          {
            if ( InternetReadFile(v15, lpBuffer, a7 - 1, &dwNumberOfBytesRead) )
            {
              lpBuffer[dwNumberOfBytesRead] = 0;
              Buffer = 200;
            }
            else
            {
              lpBuffer[dwNumberOfBytesRead] = 0;
              sub_414A30(
                0,
                0,
                "InternetReadFile Error",
                a1,
                v18,
                v19,
                v20,
                Buffer,
                dwNumberOfBytesRead,
                dwBufferLength,
                (int)hInternet,
                v25[0]);
              Buffer = 1007;
            }
          }
        }

R e c o r d R e g I n f o _ 51 A F 90 \textcolor{cornflowerblue}{RecordRegInfo\_51AF90} RecordRegInfo_51AF90函数负责加密服务器返回的数据和磁盘序列号,写入注册表。函数 G e n L i c e n s e _ 5184 E 0 \textcolor{cornflowerblue}{GenLicense\_5184E0} GenLicense_5184E0将服务器返回的数据写到

C:\ProgramData\Source Insight\4.0\si4.lic文件中,此文件就是许可证了。

后面发现,如果断开网络情况下,取消网络验证后软件会为我们创建临时许可证文件,并在下次启动软件的时候再次进行验证。

网络验证这部分如果不考虑暴力破解,是没有很好的方法过掉的。我尝试过搭建一个私服,然后模拟网络验证的过程,但这是很麻烦的事。所以说通过输入序列号的方式激活软件特别麻烦,可以考虑从导入许可证的方式激活软件。

许可证验证分析

接上面分析,临时许可证的文件数据:

<!--
	Source Insight 4.x License File

	DO NOT EDIT THIS FILE. Doing so will render it unusable.

	This license was created for:

		sad
		as
		

-->
<SourceInsightLicense>
	<LicenseProperties
		ActId="Deferred"
		Serial="S4UR-YGES-A4L4-ZH5P"
		LicensedUser="sad"
		Organization="as"
		Email=""
		Type="Standard"
		Version="4"
		MinorVersion="0"
		Date="2024-12-24"
	/>
	<Signature
		Value="HeWHWdaGlPOiOWTX6SRjSiPaKxavl+o1iDdJb9+f+LMMfy2T8DtGW4uaaxUvabW7ictaTNxXxtkZNJ68krQxjXZ8tuFHnPrMp1b8wW15HkuMqg3xHyH9anTDODKyFAmluiUcKVhU9uvPZnF1bufdpNS+zoJ9uZvVyOJi/nsz7Ac="
	/>
</SourceInsightLicense>

函数地址:0x51C680

...
CheckLicenseFile_51A510(ArgList);             // 加载许可证文件,并进行第一次检查
  if ( v2 != 200 )
    goto LABEL_8;
  if ( *ArgList == 2 )
  {
    *ArgList = 0;
    goto LABEL_12;
  }
  if ( *ArgList != 3 )
  {
    ValidateLicenseFirstPartData_51AD40((const CHAR *)ArgList + 1884, (BYTE *)ArgList + 1850);
    if ( v3 == 200 )
      goto LABEL_12;
    goto LABEL_8;
  }
  if ( CheckSignature_519CF0((int)(ArgList + 471), (char *)ArgList + 1850) != 200 || !CheckActId_51B4F0(ArgList) )
LABEL_8:
    sub_518B00(ArgList);
LABEL_12:
  --ArgList[880];
  LeaveCriticalSection((LPCRITICAL_SECTION)(ArgList + 873));
  sub_402BC0(&stru_66CFEC);
  return 1;

函数 C h e c k L i c e n s e F i l e _ 51 A 510 \textcolor{cornflowerblue}{CheckLicenseFile\_51A510} CheckLicenseFile_51A510负责解析许可证,依次验证里边的数据。

伪代码:

  ...
  v13 = 0;
  OpenAndParseLicenseFile_518B80((int)this + 1884, (int)buf);
  *((_DWORD *)this + 387) = 0;
  if ( XmlGetValue_5181E0(buf, "Type", &xmlVal) )
  {
    v3 = xmlVal;
    if ( !_stricmp(xmlVal, "Trial") )
    {
      *((_DWORD *)this + 387) = 1;
    }
    else if ( !_stricmp(v3, "Beta") )
    {
      *((_DWORD *)this + 387) = 3;
    }
    else if ( !_stricmp(v3, "Standard") )
    {
      *((_DWORD *)this + 387) = 0;
    }
  }
  if ( XmlGetValue_5181E0(buf, "LicensedUser", &xmlVal) )
  {
    strcpy((char *)this + 260, xmlVal);
    if ( XmlGetValue_5181E0(buf, "Organization", &xmlVal) )
      strcpy((char *)this + 516, xmlVal);
    if ( XmlGetValue_5181E0(buf, "Email", &xmlVal) )
      strcpy((char *)this + 772, xmlVal);
    if ( XmlGetValue_5181E0(buf, "Serial", &xmlVal) )
    {
      strcpy((char *)this + 4, xmlVal);
      if ( XmlGetValue_5181E0(buf, "ActId", &xmlVal) )
      {
        strcpy((char *)this + 1594, xmlVal);
        partialCheck = ValidateActId_403550((char *)this + 1594, byte_65AB78, 4u, 50, 7039) > 0;
        if ( !_stricmp((const char *)this + 1594, "Deferred") )
        {
          *(_DWORD *)this = 2;
          goto LABEL_39;
        }
        if ( CheckSerial_5187F0((char *)this + 4, &v11, &type, &v9, !partialCheck) )
        {
          if ( type == *((_DWORD *)this + 387) )// 判断注册类型和serial中对应标志是否相等
          {
            v5 = v9;
            if ( v9 == HIBYTE(dword_662948) && !sub_45FB00(&stru_671870, (char *)this + 4) )// 判断主版本号和软件主版本号是否相等
            {
              if ( partialCheck )
              {
                *(_DWORD *)this = 3;
              }
              else
              {
                *(_DWORD *)this = 1;
                if ( !XmlGetValue_5181E0(buf, "HWID", &xmlVal) )
                  goto LABEL_39;
                strcpy((char *)this + 1576, xmlVal);
              }
              if ( XmlGetValue_5181E0(buf, "Version", &xmlVal) )
              {
                v6 = *xmlVal;
                if ( *xmlVal >= '0' && v6 <= '9' )
                {
                  v7 = v6 - '0';
                  *((_DWORD *)this + 385) = v7;
                  if ( v7 == HIBYTE(dword_662948) && v7 == v5 )
                  {
                    *((_DWORD *)this + 390) = 0;
                    *((_DWORD *)this + 389) = 0;
                    *((_DWORD *)this + 388) = 0;
                    if ( !XmlGetValue_5181E0(buf, "Expiration", &xmlVal)// 获取许可证到期时间
                      || (sub_451EE0((int *)this + 388, xmlVal), ValidateTime_450BB0((int *)this + 388)) )// 年必须在[2013,2100]区间里
                    {
                      *((_DWORD *)this + 393) = 0;
                      *((_DWORD *)this + 392) = 0;
                      *((_DWORD *)this + 391) = 0;
                      if ( XmlGetValue_5181E0(buf, "Date", &xmlVal) )// 获取注册时间
                      {
                        sub_451EE0((int *)this + 391, xmlVal);
                        ValidateTime_450BB0((int *)this + 391);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
LABEL_39:
  v13 = -1;
  sub_518100((void **)buf);
}

主要验证内容如下:

  • Type是否为Standard,不是的话就会激活失败。

  • 如果ActIdDeferred直接返回验证失败。

  • 判断ActId,相关函数: V a l i d a t e A c t I d _ 403550 \textcolor{cornflowerblue}{ValidateActId\_403550} ValidateActId_403550

    int __cdecl sub_403550(char *a1, BYTE *a2, size_t Size, int a4, int a5)
    {
      ...
    
      v5 = 0;
      actIdPrefixTab = GenDesiredActIdPrefix_403220(a2, Size, a4, a5);// 生成一个ActId的前缀表
      if ( a4 <= 0 )
      {
    LABEL_4:
        sub_4266F0(actIdPrefixTab);
        sub_4264B0(actIdPrefixTab);
        return 0;
      }
      else
      {
        while ( 1 )
        {
          v7 = sub_446F70(*(_DWORD *)&actIdPrefixTab[4 * v5], Size, a1);// 判断ActId前Size个字节是否在actIdPrefixTbl中
          if ( v7 == Size )
            break;
          if ( ++v5 >= a4 )
            goto LABEL_4;
        }
        sub_4266F0(actIdPrefixTab);
        sub_4264B0(actIdPrefixTab);
        return v7;
      }
    }
    
  • ActId前缀(前4个字节)在actIdPrefixTbl中时,partialCheckfalse,表示需要校验 s e r i a l [ 15 : 18 ] \textcolor{orange}{serial[15:18]} serial[15:18]这部分数据,同时设置 ∗ A r g L i s t = 3 \textcolor{orange}{*ArgList = 3} ArgList=3

  • 根据partialCheck进行部分或者完全校验4.0serial

  • 注册时间和过期时间的年必须在区间 [ 2013 , 2100 ] \textcolor{orange}{[2013,2100]} [20132100]中,月、日必须在合法范围。

回到上层调用 s u b _ 51 C 680 \textcolor{cornflowerblue}{sub\_51C680} sub_51C680函数中,如果 ∗ A r g L i s t = = 3 \textcolor{orange}{*ArgList == 3} ArgList==3就会验证SignatureActId,对应函数 C h e c k S i g n a t u r e _ 519 C F 0 \textcolor{cornflowerblue}{CheckSignature\_519CF0} CheckSignature_519CF0 C h e c k A c t I d _ 51 B 4 F 0 \textcolor{cornflowerblue}{CheckActId\_51B4F0} CheckActId_51B4F0;如果 ∗ A r g L i s t = = 1 \textcolor{orange}{*ArgList == 1} ArgList==1就会检查许可证 <Signature> 节点前的数据,对应函数 V a l i d a t e L i c e n s e F i r s t P a r t D a t a _ 51 A D 40 \textcolor{cornflowerblue}{ValidateLicenseFirstPartData\_51AD40} ValidateLicenseFirstPartData_51AD40

Signature的验证

Signature的值是根据许可证中 <Signature> 节点前的数据生成的,生成方式见函数 G e n D e s i r e d S i g n a t u r e _ 4031 F 0 \textcolor{cornflowerblue}{GenDesiredSignature\_4031F0} GenDesiredSignature_4031F0。这个函数不需要逆向分析,用到时直接抠出来。只要保证 <Signature> 借点钱的数据合法,那么计算出来的signature自然就是正确的。验证时,也是用许可证中的 <Signature> 节点的数据与生成的signature值进行对比。

ActId的验证

相关函数 C h e c k A c t I d _ 51 B 4 F 0 \textcolor{cornflowerblue}{CheckActId\_51B4F0} CheckActId_51B4F0

...
v2 = ValidateActId_403550((int)(this + 1594), (int)&byte_65AB78, 4u, 50, 7039);// 验证ActId前缀
return v2 && MakeActIdLow5Bytes_51AE80(Str2, 0) && strcmp(&this[v2 + 1594], Str2) == 0;// 验证ActId后4个字节
  • 取许可证中ActId数据的前两个字节,判断是否在actIdPrefixTbl中,不是就验证失败。actIdPrefixTbl的生成见函数 G e n D e s i r e d A c t I d P r e f i x _ 403220 \textcolor{cornflowerblue}{GenDesiredActIdPrefix\_403220} GenDesiredActIdPrefix_403220,这个函数不需要分析,只需要模拟就能得出actIdPrefixTbl
int __cdecl MakeActIdLow4Bytes_51AE80(char *ActId, int a2)
{
  int v3; // [esp+0h] [ebp-314h] BYREF
  char v4[784]; // [esp+4h] [ebp-310h] BYREF

  FetchSystemVerInfo_518A40(v4);                // 取系统版本号
  if ( GetSystemInfo_518C10(v4) )               // 取系统盘序列号、当前登录用户的SID和计算机名
  {
    GenActId_518430(v4, (BYTE *)4, (BYTE *)&v3, a2);// a2 == 0
    sprintf(ActId, "%u", v3);
    return 1;
  }
  else
  {
    *ActId = 0;
    return 0;
  }
}
  • 根据系统盘序列号、当前登录用户的SID和计算机名生成正确的ActId后4字节数据。生成算法伪代码:

    void __cdecl sub_518430(const char *a1, BYTE *signature, BYTE *a3, int a4)
    {
      size_t v4; // eax
      char Str[512]; // [esp+0h] [ebp-200h] BYREF
    
      sprintf(Str, "%s%s%s", a1, a1 + 256, a1 + 512);// 拼接系统盘序列号+当前登录用户Sid+计算机名,结果保存在Str中
      v4 = strlen(Str);
      Encode_4031F0((BYTE *)Str, v4, a4 != 0 ? 0x42A : 0x7A9, signature, a3);// 编码Str
    }
    
  • 将编码后的Str转为文本型的十进制数就是最终的ActId后4字节数据。

激活

基于上面的分析,可以不需要暴力破解,通过生成合法的License激活软件,因为通过License去激活不需要走网络验证。

而License文件内容有3个关键点:

  1. serial,也就是4.0的序列号。需要生成合法的序列号。
  2. ActId,需要生成合法的ActId。
  3. Signature,需要生成合法的签名。

验证算法研究请见我的github:https://github.com/singlefreshBird/Crack/tree/resource_insight40

Source Insight4.0免费版安装包见我的百度网盘: https://pan.baidu.com/s/1pYuPhVRolxH-rwjyDGYqsg?pwd=dq8j ,提取码: dq8j

最终效果:

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值