前言
这里po jie的是当前最新版本Source Insight 4.0.118,总结po jie过程有以下三个部分:
- 逆向Serial Number序列号验证算法,构造可用序列号
- 逆向分析验证license签名过程,自签名license文件,并使用自己的公钥替换程序中原始公钥
- 程序运行起来后,会单独开启一个线程在线检查Serial Number是否有效,需要修改二进制文件过掉此检查
除此之外,笔者也看了一些大佬的文章,提到有黑名单检查,因为没遇到这个逻辑,程序暂时也能用,所以这里的破解中没有涉及,后面如果遇到再研究也不迟。
文中用到的所有代码可点击右侧超链接,在 Github上查看
环境准备
IDA6.8、x32dbg
官网 下载sourceinsight40118-setup.exe
<br>
0、简单po jie:修改两字节
因为本文主要是研究自签名license并替换程序中公钥的po jie方法,比较麻烦,纯粹是为了学习目的。如果对这种方法不感兴趣,这部分内容就提供另一种简单的po jie方法:修改 sourceinsight4.exe 二进制文件中的两个字节完成po jie
- 第一处是 VA=005141A8,FA=1135A8,将 74 改为 EB,原因可参考 第 2.2 节
- 第二处是 VA=00513470,FA=112870,将 83 改为 C3,原因可参考 第 3 节
最后,复制本文 2.3节 中 si4.lic 的内容,并保存为 C:\ProgramData\Source Insight\4.0\si4.lic 文件即可po jie程序
<br>
1、验证序列号的过程——逆向分析
这部分内容分4个小节:
1.1 节 讲述如何定位到验证序列号的代码
1.2 节 中逆向还原验证序列号格式的函数,并按照格式构造出正式版序列号
1.3 节 中逆向还原本地校验序列号的函数,构造出可通过本地校验的序列号
1.4 节 本地验证序列号
1.1 定位“Serial Number错误”弹窗代码
安装程序后,打开sourceinsight4.exe程序会看到选择license文件的界面,如下图:
选择第一项“输入serial number”,点击“Next >”继续,显示输入Serial Number的对话框,并且提示 Serial Number 的格式“S4XX-XXXX-XXXX-XXXX”。按照格式输入一个,点击"Next>",会弹出“无效的Serial Number”警告,如下图:
在IDA中搜索弹窗警告中的文本“The serial number ...”,找到引用此字符串的地址是 .text:00513A69。
分析.text:00513A69此位置上下文代码,可以看出 .text:00513A45 call sub_510B50 是Serial Number的验证函数,若验证失败(返回结果0),则会再判断是否是3.x版本的Serail Number,不是则弹出上图中的警告;若验证成功则跳转到 loc_513ACB 继续判断验证结果。代码如下所示:
.text:00513A25 mov eax, dword_673488.text:00513A2A push 1 ; int.text:00513A2C lea ecx, [eax+604h].text:00513A32 push ecx ; int.text:00513A33 lea edx, [eax+60Ch].text:00513A39 push edx ; int.text:00513A3A add eax, 608h.text:00513A3F push eax ; int.text:00513A40 lea eax, [esp+158h+MultiByteStr].text:00513A44 push eax ; char *.text:00513A45 call sub_510B50 ; 验证Serial Number是否正确.text:00513A4A add esp, 34h.text:00513A4D test eax, eax ; eax = 0, 弹出错误窗口并返回.text:00513A4D ; eax!= 0, 继续验证Serial Number格式.text:00513A4F jnz short loc_513ACB.text:00513A51 lea ecx, [esp+128h+MultiByteStr].text:00513A55 push ecx.text:00513A56 call sub_561CB0.text:00513A5B add esp, 4.text:00513A5E test eax, eax.text:00513A60 jz short loc_513A69.text:00513A62 push offset aTheSerialNumbe ; "The serial number you entered is for ve"....text:00513A67 jmp short loc_513A6E.text:00513A69 ; ---------------------------------------------------------------------------.text:00513A69.text:00513A69 loc_513A69: ; CODE XREF: sub_5139C0+A0j.text:00513A69 push offset aTheSerialNum_0 ; "The serial number you entered is not co"....text:00513A6E.text:00513A6E loc_513A6E: ; CODE XREF: sub_5139C0+A7j.text:00513A6E call sub_40AC20.text:00513A73 add esp, 4... ....text:00513ACA retn.text:00513ACB ; ---------------------------------------------------------------------------.text:00513ACB.text:00513ACB loc_513ACB: ; CODE XREF: sub_5139C0+8Fj... ....text:00513AE1 cmp [eax+604h], edx.text:00513AE7 jz short loc_513B0D.text:00513AE9 push offset aTheSerialNum_1 ; "The serial number you entered is for a "....text:00513AEE call sub_40AC20... ....text:00513B0C retn
<br>
1.2 Serial Number验证函数 分析
详细分析上面步骤中提到的 sub_510B50 处的序列号验证函数,总结出Serial Number的验证规则如下:
SerailNumber字符串长度必须为19(16个字符加上3个分隔符)
SerailNumber[0]必须等'S'
SerailNumber[1]是'0'-'9'
SerailNumber[2]等于'T'表示Trial license试用许可,等于'B'表示Beta license测试许可,等于'S'表示Standard license标准许可,等于'U'表示Upgrade license升级许可
SerailNumber[3]等于 'G'、'V'、'R' 其中之一
SerailNumber[6]等于 'R'、'G'、'D'、'F' 其中之一
SerialNumber的前12个字符经过函数 .text:00510C6B call sub_510320 转换后得到4个字符,与SerialNumber的最后4个字符必须相同
代码如下所示:
.text:00510B50 ; int __cdecl sub_510B50(char *, int, int, int, int).text:00510B50 sub_510B50 proc near ; CODE XREF: .text:005129C4p.text:00510B50 ; sub_5139C0+85p.text:00510B50.text:00510B50 ary4char = dword ptr -18h.text:00510B50 szSNTemp = byte ptr -14h.text:00510B50 ptr_szSN = dword ptr 4.text:00510B50 .text:00510B50 ptr_nSN[3]Flag = dword ptr 8.text:00510B50 ptr_nLicTypeFlag= dword ptr 0Ch.text:00510B50 ptr_nVersionFlag= dword ptr 10h.text:00510B50 arg_10 = dword ptr 14h.text:00510B50.text:00510B50 sub esp, 18h.text:00510B53 push esi.text:00510B54 mov esi, [esp+1Ch+ptr_szSN].text:00510B58 push esi ; char *.text:00510B59 call __strupr.text:00510B5E push esi ; char *.text:00510B5F call _strlen.text:00510B64 add esp, 8.text:00510B67 cmp eax, 13h ; strlen(ptr_szSN) == 13h.text:00510B6A jnz loc_510C86.text:00510B70 mov al, '-'.text:00510B72 cmp [esi+4], al ; ptr_szSN[4] = '-'.text:00510B75 jnz loc_510C86.text:00510B7B cmp [esi+9], al ; ptr_szSN[9] = '-'.text:00510B7E jnz loc_510C86.text:00510B84 cmp [esi+0Eh], al ; ptr_szSN[14] = '-'.text:00510B87 jnz loc_510C86.text:00510B8D cmp byte ptr [esi], 'S' ; ptr_szSN[0] = 'S'.text:00510B90 jnz loc_510C86.text:00510B96 mov ecx, [esp+1Ch+arg_10].text:00510B9A test ecx, ecx.text:00510B9C jz short loc_510BB5.text:00510B9E mov al, [esi+6].text:00510BA1 cmp al, 'R' ; ptr_szSN[6] == 'R'.text:00510BA3 jz short loc_510BB5.text:00510BA5 cmp al, 'G' ; ptr_szSN[6] == 'G'.text:00510BA7 jz short loc_510BB5.text:00510BA9 cmp al, 'D' ; ptr_szSN[6] == 'D'.text:00510BAB jz short loc_510BB5.text:00510BAD cmp al, 'F' ; ptr_szSN[6] == 'F'.text:00510BAF jnz loc_510C86.text:00510BB5.text:00510BB5 loc_510BB5: ; CODE XREF: sub_510B50+4Cj.text:00510BB5 ; sub_510B50+53j ....text:00510BB5 mov al, [esi+1].text:00510BB8 cmp al, '0' ; ptr_szSN[1] >= '0'.text:00510BBA jl loc_510C86.text:00510BC0 cmp al, '9' ; ptr_szSN[1] <= '9'.text:00510BC2 jg loc_510C86.text:00510BC8 mov edx, [esp+1Ch+ptr_nVersionFlag].text:00510BCC movsx eax, al.text:00510BCF sub eax, '0'.text:00510BD2 mov [edx], eax.text:00510BD4 mov al, [esi+2].text:00510BD7 cmp al, 'T' ; ptr_szSN[2] == 'T', Trial license.text:00510BD9 jnz short IF_BEGIN.text:00510BDB mov eax, [esp+1Ch+ptr_nLicTypeFlag].text:00510BDF mov dword ptr [eax], 1.text:00510BE5 jmp short ELSE_END.text:00510BE7 ; ---------------------------------------------------------------------------.text:00510BE7.text:00510BE7 IF_BEGIN: ; CODE XREF: sub_510B50+89j.text:00510BE7 cmp al, 'B' ; ptr_szSN[2] == 'B', Bete license, cannot be used with the release version.text:00510BE9 jnz short ELSE_IF.text:00510BEB mov edx, [esp+1Ch+ptr_nLicTypeFlag].text:00510BEF mov dword ptr [edx], 3.text:00510BF5 jmp short ELSE_END.text:00510BF7 ; ---------------------------------------------------------------------------.text:00510BF7.text:00510BF7 ELSE_IF: ; CODE XREF: sub_510B50+99j.text:00510BF7 cmp al, 'S' ; ptr_szSN[2] == 'S', Standard license.text:00510BF9 jnz short ELSE_IF_.text:00510BFB mov eax, [esp+1Ch+ptr_nLicTypeFlag].text:00510BFF mov dword ptr [eax], 0.text:00510C05 jmp short ELSE_END.text:00510C07 ; ---------------------------------------------------------------------------.text:00510C07.text:00510C07 ELSE_IF_: ; CODE XREF: sub_510B50+A9j.text:00510C07 cmp al, 'U' ; ptr_szSN[2] == 'U', Upgrade License.text:00510C09 jnz short loc_510A56.text:00510C0B mov edx, [esp+1Ch+ptr_nLicTypeFlag].text:00510C0F mov dword ptr [edx], 0.text:00510C15.text:00510C15 ELSE_END: ; CODE XREF: sub_510B50+95j.text:00510C15 ; SI_ValidateSNFmt+A5j ....text:00510C15 mov al, [esi+3].text:00510C18 cmp al, 'G' ; ptr_szSN[3] == 'G'.text:00510C1A jnz short IF_BEGIN2.text:00510C1C mov eax, [esp+1Ch+ptr_nSN[3]Flag].text:00510C20 mov dword ptr [eax], 1.text:00510C26 jmp short ELSE_END2.text:00510C28 ; ---------------------------------------------------------------------------.text:00510C28.text:00510C28 IF_BEGIN2: ; CODE XREF: sub_510B50+CAj.text:00510C28 cmp al, 'V' ; ptr_szSN[3] == 'V'.text:00510C2A jnz short ELSE_IF2.text:00510C2C mov edx, [esp+1Ch+ptr_nSN[3]Flag].text:00510C30 mov dword ptr [edx], 2.text:00510C36 jmp short ELSE_END2.text:00510C38 ; ---------------------------------------------------------------------------.text:00510C38.text:00510C38 ELSE_IF2: ; CODE XREF: sub_510B50+DAj.text:00510C38 cmp al, 'R' ; ptr_szSN[3] == 'R'.text:00510C3A jnz short loc_510C86.text:00510C3C mov eax, [esp+1Ch+ptr_nSN[3]Flag].text:00510C40 mov dword ptr [eax], 0.text:00510C46.text:00510C46 ELSE_END2: ; CODE XREF: sub_510B50+D6j.text:00510C46 ; sub_510B50+E6j.text:00510C46 test ecx, ecx.text:00510C48 jz short loc_510C7C.text:00510C4A lea ecx, [esp+1Ch+szSNTemp].text:00510C4E push esi ; char *.text:00510C4F push ecx ; char *.text:00510C50 call _strcpy.text:00510C55 lea edx, [esp+24h+ary4char].text:00510C59 push edx.text:00510C5A push offset ary256Chars.text:00510C5F lea eax, [esp+2Ch+szSNTemp].text:00510C63 push 0Fh.text:00510C65 push eax.text:00510C66 mov [esp+34h+szSNTemp+0Fh], 0.text:00510C6B call sub_510320 ; 通过SerialNumber前12个字符计算出最后4个字符,并与输入的SerialNumber最后4个字符做比较.text:00510C70 mov ecx, [esi+0Fh].text:00510C73 add esp, 18h.text:00510C76 cmp ecx, [esp+1Ch+ary4char] ; 比较计算出的最后4个字符是否与输入的SerialNumber最后4个字符相同.text:00510C7A jnz short loc_510C86.text:00510C7C.text:00510C7C loc_510C7C: ; CODE XREF: sub_510B50+F8j.text:00510C7C mov eax, 1.text:00510C81 pop esi.text:00510C82 add esp, 18h.text:00510C85 retn.text:00510C86 ; ---------------------------------------------------------------------------.text:00510C86.text:00510C86 loc_510C86: ; CODE XREF: sub_510B50+1Aj.text:00510C86 ; sub_510B50+25j ....text:00510C86 xor eax, eax.text:00510C88 pop esi.text:00510C89 add esp, 18h.text:00510C8C retn.text:00510C8C sub_510B50 endp