lakshmi 发表于 2018-3-19 18:25

Source Insight 4.0 破解过程

本帖最后由 lakshmi 于 2018-3-20 08:33 编辑

0x1 写在前面
    由于最近准备向各位大牛们看齐,学习学习Linux源码,需要用到source insight ,因此就在网上找破解版的si,这个帖子已经给出了破解版 https://www.52pojie.cn/thread-580580-1-1.html
但是需要修改二进制文件,作为一个怀疑整个世界的渣,写了这么一篇帖子(不过最后本渣也还是修改了二进制文件{:1_896:}),仅供学习交流!!!
    准备环境:
    Windows 7 32 bit
    百度上Source Insight 4.0 (Version 4.00.0086 built on 2017-04-27) (官网最新版是 4.00.0089),查看了一下破解过程相同
    IDA 7.0
    52破解专版OllDbg
    UltraEdit32

0x2 破解过程
    首先,查看PE文件,发现没有加壳,运行起来,提示需要输入注册码,如下图所示:



点击下一步,然后随意输入注册码,点击确定,如下提示:

有提示,那就按套路出牌, 用IDA打开文件,C:\Program Files\Source Insight 4.0\sourceinsight4.exe,搜索字符串:
int __cdecl register_function(int a1, int a2, int a3)
{
   。。。。。。。。。。。。。。。。
if ( !check_register(&str_input, dword_1361930 + 0x608, dword_1361930 + 0x60C, dword_1361930 + 0x604, 1) ){
    Error_SerialNumber(
      "The serial number you entered is not correct.\n\nPlease check the number and re-enter it.",
      v11,
      v12,
      *(int *)v13,
      v14,
      v15,
      v16,
      v17,
      v18);
    sub_1106A20(a1, 30);
    sub_1104660();
    if ( ++dword_13619CC >= sub_114D9A0(1) + 3 )
    {
      dword_13619CC = 0;
      sub_114C530();
      Sleep(0x1B58u);
      sub_114C580();
      return 0;
    }
    return 0;
}
dword_13619CC = 0;
if ( *(_DWORD *)(dword_1361930 + 0x604) != HIBYTE(dword_13446B0) )
{
    Error_SerialNumber(
      "The serial number you entered is for a different version of Source Insight.\n"
      "\n"
      "This version requires a version 4.x serial number.",
      v11,
      v12,
      *(int *)v13,
      v14,
      v15,
      v16,
      v17,
      v18);
    sub_1106A20(a1, 30);
    sub_1104660();
    return 0;
}
if ( *(_DWORD *)(dword_1361930 + 0x60C) == 3 )
{
    Error_SerialNumber(
      "The serial number cannot be used with the 'release' version of Source Insight.",
      v11,
      v12,
      *(int *)v13,
      v14,
      v15,
      v16,
      v17,
      v18);
    sub_1104660();
    return 0;
}
if ( !sub_120AF10(&str_input) )
    return 0;
if ( sub_120C670(dword_1361930 + 0x504) )
{
    sub_11122A0("Upgrade License was validated: %s", dword_1361930 + 1284, v11, v12, *(int *)v13, v14, v15, v16, v17);
}
else
{
    Error_SerialNumber(
      "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.",
      v11,
      v12,
      *(int *)v13,
      v14,
      v15,
      v16,
      v17,
      v18);
    sub_1106A20(a1, 30);
    sub_1104660();
    sub_11121C0("Upgrade License could not be validated", v4, v5, v6, v7, v12, *(int *)v13, v14, v15);
}
return 0;
}
只有当 check_register(&str_input, dword_1361930 + 0x608, dword_1361930 + 0x60C, dword_1361930 + 0x604, 1) 返回值非0时表示验证成功, 接着查看这个该函数:
BOOL __cdecl check_register(char *sn, int a2, int a3, int a4, int one)
{
BOOL result; // eax
char v6; // al
char v7; // al
char v8; // al
char v9; // al
int v10; //
char v11; //
char v12; // _strupr(sn);
if ( strlen(sn) != 0x13 || sn != '-' || sn != '-' || sn != '-' || *sn != 'S' )// format: SXXX-XXX-XXXX-XXXX
    return 0;
if ( one )
{
    v6 = sn;
    if ( v6 != 'R' && v6 != 'G' && v6 != 'D' && v6 != 'F' )// SXXX-X(R|G|D|F)XX-XXXX-XXXX
      return 0;
}
v7 = sn;
if ( v7 < '0' || v7 > '9' )                   // S(0-9)XX-X(R|G|D|F)XX-XXXX-XXXX
    return 0;
*(_DWORD *)a4 = v7 - '0';
v8 = sn;
switch ( v8 )                                 // S(0-9)(T|B|S|U)X-X(R|G|D|F)XX-XXXX-XXXX
{
    case 'T':
      *(_DWORD *)a3 = 1;
      break;
    case 'B':
      *(_DWORD *)a3 = 3;
      break;
    case 'S':
      *(_DWORD *)a3 = 0;
      break;
    case 'U':
      *(_DWORD *)a3 = 0;
      break;
    default:
      return 0;
}
v9 = sn;
if ( v9 != 'G' )
{
    if ( v9 == 'V' )
    {
      *(_DWORD *)a2 = 2;
      goto LABEL_28;
    }
    if ( v9 == 'R' )
    {
      *(_DWORD *)a2 = 0;
      goto LABEL_28;
    }
    return 0;
}
*(_DWORD *)a2 = 1;
LABEL_28:
result = 0;
if ( !split_key_compute_function(sn + 5, 4u, 0)// split_key_compute_function(XX, 4u, 0) == 0
    && !split_key_compute_function(sn + 10, 4u, 0)
    && !split_key_compute_function(sn + 0xF, 4u, 0) )
{
    if ( !one
      || (strcpy(&v11, sn),
          v12 = 0,
          compute_last_four_byte(&v11, 0xFu, (int)byte_12F4A48, (int)&v10),
          *(_DWORD *)(sn + 0xF) == v10) )
    {
      result = 1;
    }
}
return result;
}


简单介绍上边的函数,首先是判断序列号的合法性,必须满足如下格式(其中X表示未知字符):
S(0-9)(T|B|S|U)X-X(R|G|D|F)XX-XXXX-XXXX
其中第二位,根据版本要求必须是4,第三位中T 表示 试用版,B表示Beta版,S表示Standard版,U未知。因此按照本软件的要求,序列号格式应该如下:
S4SX-X(R|G|D|F)XX-XXXX-XXXX
接着通过split_key_compute_function对序列号中按“-”分开的四位分别验证,其返回值必须是0,这个函数很好绕过,只要满足这4个字节中的第一个字节是字符即可满足(没什么作用{:1_909:}),因此在最新版中已经将这块给省掉了。
接着是
          compute_last_four_byte(&v11, 0xFu, (int)byte_12F4A48, (int)&v10),
          *(_DWORD *)(sn + 0xF) == v10) )

compute_last_four_byte函数的作用是利用替换查表的方法,将sn中的前15个字节计算出一个四字节的数,这个四字节的数就是sn的最后4字节,具体代码如下:
BYTE char_table = { 'K', 'V', '9', '6', 'G', 'M', 'J', 'Y', 'H', '7', 'Q', 'F', '5', 'T', 'C', 'W', '4', 'U', '3', 'X', 'Z', 'P', 'R', 'S', 'D', 'N'};
BYTE byte_table = {
    0x23, 0xDD, 0x78, 0xB5, 0x33, 0x6F, 0xD4, 0xF9, 0xA6, 0xE8, 0xCC, 0x7C, 0x9F, 0xB3, 0x22, 0xDA,
0x32, 0xDF, 0x71, 0xB7, 0x61, 0x3D, 0x6B, 0x57, 0xD7, 0xA1, 0x34, 0x38, 0xF2, 0xE1, 0xF3, 0xB8,
0x1A, 0x80, 0xF5, 0xFE, 0x91, 0x01, 0x3C, 0x73, 0x93, 0x48, 0xA0, 0xE0, 0x94, 0xAA, 0x39, 0x8F,
0x58, 0xE2, 0x31, 0x0B, 0xBB, 0xCE, 0x4C, 0xD2, 0x56, 0xC2, 0x5E, 0x27, 0xB6, 0xFB, 0x65, 0xAE,
0x55, 0x60, 0xBD, 0x10, 0x86, 0xF7, 0xC1, 0x88, 0x12, 0xED, 0x67, 0xC4, 0x74, 0x30, 0x1B, 0xBC,
0x9A, 0xB0, 0xEF, 0x36, 0xC5, 0x72, 0x5B, 0x7E, 0x54, 0x2C, 0x0F, 0xF6, 0xA9, 0x85, 0x2A, 0xB1,
0x37, 0xF1, 0x2F, 0x4E, 0xE7, 0x6A, 0x75, 0xA8, 0x26, 0xEB, 0x3F, 0x6C, 0x69, 0x20, 0x87, 0x62,
0x8D, 0x68, 0xA5, 0xFA, 0x3A, 0x04, 0x21, 0x1F, 0xAC, 0x05, 0xA4, 0x76, 0x11, 0x70, 0x9E, 0x46,
0x24, 0x5D, 0xC6, 0xE4, 0x95, 0x82, 0x1C, 0xBA, 0x59, 0x09, 0xD9, 0x44, 0x98, 0x92, 0x07, 0xAF,
0xA7, 0x41, 0x96, 0x90, 0xB4, 0x42, 0x63, 0x99, 0xD0, 0x4D, 0x97, 0xBE, 0x40, 0xCF, 0x84, 0xE5,
0x1D, 0x5A, 0x0C, 0x7F, 0xC7, 0xEA, 0xEE, 0xEC, 0x00, 0xD5, 0x49, 0x2D, 0x51, 0xAD, 0xB9, 0x89,
0x77, 0x52, 0x3E, 0x8C, 0xE6, 0xFF, 0x15, 0xDE, 0x6D, 0x14, 0xA2, 0xCD, 0xA3, 0xD6, 0x17, 0x81,
0xC8, 0x45, 0x4B, 0x35, 0x0A, 0x0D, 0xFC, 0x9D, 0x16, 0x3B, 0xD3, 0x7D, 0xD1, 0xF4, 0xFD, 0xCA,
0x25, 0x06, 0x6E, 0xF8, 0x5F, 0xBF, 0x8A, 0x7B, 0x50, 0xD8, 0x79, 0x9C, 0xAB, 0x43, 0x53, 0xCB,
0x8E, 0x4F, 0xE3, 0xC9, 0x8B, 0xDC, 0x5C, 0xC0, 0x1E, 0x9B, 0x18, 0x02, 0x47, 0x03, 0x2B, 0x0E,
0x66, 0x4A, 0xB2, 0xF0, 0xE9, 0x19, 0x29, 0x7A, 0xC3, 0x08, 0x83, 0xDB, 0x64, 0x13, 0x2E, 0x28 };

intcompute_last_four_byte(BYTE *keystr,DWORD key_length, int table, int retvalue)
{
unsigned int value_index; // esi
BYTE v5; // cl
unsigned int iIndex; // eax
int result; // eax
value_index = 0;
do
{
v5 = *(BYTE *)((BYTE)(value_index + *keystr) + table);
for (iIndex = 1; iIndex< key_length; ++iIndex)
   v5 = *(BYTE *)((v5 ^ (char)keystr) + table);
result = retvalue;
*(BYTE *)(++value_index + retvalue - 1) = char_table;
} while (value_index < 4);
return result;
}

既然后四字节可以算出来,那么此处的破解思路就是,按照格式S4SX-X(R|G|D|F)XX-XXXX-XXXX要求,固定前如下字节 S4SV-UFWT-ZPRX-,那么通过穷举X(此处为了缩小范围,直接遍历char_table中的字符即可),来计算出最后四个字节,在选出满足要求的序列号即可,代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
int res;
CHAR serial_number = { 'S', '4', 'S', 'V', '-', 'U', 'F', 'W', 'T', '-', 'Z', 'P', 'R', 0x0, '-', 0x0, 0x0, 0x0, 0x0, 0x0 };

for (DWORD dwIndex = 0; dwIndex < 26; dwIndex++)
{
serial_number = char_table;
compute_last_four_byte((BYTE *)serial_number, 0xf, (int)byte_table, (int)&res);
memcpy(serial_number + 0xf, (BYTE *)&res, 4);
if (serial_number >= 0x41)
   printf("%s\n", serial_number);
}
}

结果如下:


任选一组,调试发现成功绕过,但是此处还是没有完全破解成功,因为,source insight 需要一个license文件,文件保存在C:\programdata\source insigth\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:

lakshmi
52pojie
XXX@XX.com

-->
<SourceInsightLicense>
<Header
Value="1"
/>
<LicenseProperties
LicensedUser="lakshmi"
ActId="9980973922"
HWID="XXXXXXXXX"
Serial="S4SV-UFWT-ZPR6-F336"
Organization="52pojie"
Email="XXX@XX.com"
Type="Standard"
Version="4"
MinorVersion="0"
Date="2018-03-12 00:00:00"
Expiration="2028-09-12 00:00:00"
/>
<Signature
Value="az2bGw1ks1ftqXzf/tVrq071RbJqclxWljohEAcOwvx3pimSMZa/isSdpIRM5vXsTt/ZVeD62r8SlzvNlt/5C6XzPTMKvik+fzOakeM8zQNSCmJ/W1MQ9WgXZyiQpp3VnZgkrO3Ut/YHRWCe4zPJjcPJZ7tXf62Lvxx5MO29uEqHCvc8ZHUGg0wafuYooRoeUWzLneTisGDnfAy3UTaH3oxdqwlMFbDa10sVEEbW2WTzZFxL826r4lAGmH9MUCiu/3R/tpvrYlyWRv+29kQoqGlkwQUV4B1iL6Dyp5bC17JSYpChsjdm9JkoEg6HRsA3WtdQBRecA65Jyhr8XbMPFg=="
/>
</SourceInsightLicense>

source insight 会根据自带公钥,对license文件中的 除了Signature中的数据进行签名,并与变换后的Signature值进行比较,具体怎么变换没有去跟踪,反正臣妾都破不了签名,最后还是手贱地选择了修改二进制文件
int __cdecl sub_50B440(BYTE *pbData, DWORD dwDataLen, BYTE *pbSignature, DWORD dwSigLen)
{
BOOL v5; // esi
HCRYPTHASH phHash; //
HCRYPTPROV phProv; //
struct _CERT_PUBLIC_KEY_INFO *pvStructInfo; //
HCRYPTKEY phKey; //
DWORD pcbBinary; //
DWORD pcbStructInfo; //
BYTE pbBinary; //
phHash = 0;
phKey = 0;
pcbBinary = 2048;
phProv = 0;
if ( !CryptStringToBinaryA(pszString, 0, 0, &pbBinary, &pcbBinary, 0, 0)
    || !CryptDecodeObjectEx(1u, (LPCSTR)8, &pbBinary, pcbBinary, 0x8000u, 0, &pvStructInfo, &pcbStructInfo) )
{
    return 472;
}
if ( !CryptAcquireContextW(&phProv, 0, 0, 1u, 0xF0000000) )
    return 473;
if ( !CryptImportPublicKeyInfo(phProv, 1u, pvStructInfo, &phKey) )
    return 474;
LocalFree(pvStructInfo);
if ( !CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )
    return 474;
if ( !CryptHashData(phHash, pbData, dwDataLen, 0) )
    return 475;
v5 = CryptVerifySignatureW(phHash, pbSignature, dwSigLen, phKey, 0, 0);
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return v5 != 0 ? 0xC8 : 0x1CE;
}
让函数最终直接返回0xC8即可
最终效果如下:

stxxb 发表于 2018-3-19 21:17

楼主厉害啊

MistHill 发表于 2018-3-22 12:11

lakshmi 发表于 2018-3-20 08:31
嗯,不过@MistHill 大神的那个key,被新版的4.0.86-4.0.89给强制过滤掉了
这几个版本直接内嵌了n多key, ...

嗯,最初只需要替换公钥就可以了。

后来它加入了:更新信息黑名单检查、程序硬编码黑名单检查、定时在线检查。

代码只需要改动4个字节就可以过掉这些检查。

swjtu_ray 发表于 2018-3-19 18:27

给大佬递茶,谢谢分享

起点陌离 发表于 2018-3-19 18:34

一直很想学习这个,看起来确实很难呀!

gusha915 发表于 2018-3-19 18:48

感谢分享,,,

大喵 发表于 2018-3-19 18:55

可以可以

lggjlpph 发表于 2018-3-19 19:06

ping170 发表于 2018-3-19 19:26

大佬辛苦了,感谢分享

Atomi 发表于 2018-3-19 19:27

感谢分享            

elpsycongroo001 发表于 2018-3-19 21:00

感谢分享,大佬辛苦了
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Source Insight 4.0 破解过程