Windows逆向学习笔记——破解Source Insight 4
前言
这里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<br>
1.3 构造可用的Serial Number
步骤1.2中提到Serial Number的验证规则之一是:序列号的前12个字符经过函数 sub_510320 转换后得到4个字符,与序列号的最后4个字符相同。
以下便是是 sub_510320 汇编代码分析以及据此还原的C代码,将此函数命名为 Get4charBySNPre12char
.text:00510320 sub_510320      proc near               ; CODE XREF: sub_510390+73p.text:00510320                                         ; sub_510B50+11Bp ....text:00510320.text:00510320 ptr_szSN        = dword ptr  4.text:00510320 nSNLen          = dword ptr  8.text:00510320 ary256char      = dword ptr  0Ch.text:00510320 ptr_4charsResult= dword ptr  10h.text:00510320.text:00510320                 push    ebx.text:00510321                 mov     ebx, [esp+4+nSNLen].text:00510325                 push    ebp.text:00510326                 mov     ebp, [esp+8+ptr_szSN].text:0051032A                 push    esi.text:0051032B                 push    edi.text:0051032C                 mov     edi, [esp+10h+ary256char].text:00510330                 xor     esi, esi.text:00510332.text:00510332 DO_BEGIN:                               ; CODE XREF: sub_510320+5Ej.text:00510332                 movsx   eax, byte ptr [ebp+0].text:00510336                 add     eax, esi.text:00510338                 and     eax, 0FFh.text:0051033D                 mov     cl, [eax+edi].text:00510340                 mov     eax, 1.text:00510345                 cmp     ebx, eax.text:00510347                 jbe     short loc_510361.text:00510349                 lea     esp, [esp+0].text:00510350.text:00510350 _DO_BEGIN:                              ; CODE XREF: sub_510320+3Fj.text:00510350                 movsx   edx, byte ptr [eax+ebp].text:00510354                 movzx   ecx, cl.text:00510357                 xor     edx, ecx.text:00510359                 mov     cl, [edx+edi].text:0051035C                 inc     eax.text:0051035D                 cmp     eax, ebx.text:0051035F                 jb      short _DO_BEGIN.text:00510361.text:00510361 _DO_END:                                ; CODE XREF: sub_510320+27j.text:00510361                 movzx   eax, cl.text:00510364                 cdq.text:00510365                 mov     ecx, 1Ah.text:0051036A                 idiv    ecx.text:0051036C                 mov     eax, [esp+10h+ptr_4charsResult].text:00510370                 inc     esi.text:00510371                 mov     dl, byte ptr ds:sz26Chars[edx] ; "KV96GMJYH7QF5TCW4U3XZPRSDN".text:00510377                 mov     [esi+eax-1], dl.text:0051037B                 cmp     esi, 4.text:0051037E                 jb      short DO_BEGIN.text:00510380                 pop     edi.text:00510381                 pop     esi.text:00510382                 pop     ebp.text:00510383                 pop     ebx.text:00510384                 retn.text:00510384 sub_510320      endp//还原为C代码char g_sz26Chars[] = { "KV96GMJYH7QF5TCW4U3XZPRSDN" };void Get4charBySNPre12char(char* szSN, int nSNLen, char* ary256Chars, char* pResult) {    for (int i = 0; i < 4; i++) {        char cl = ary256Chars[(szSN[0] + i) & 0xFF];        for (int j = 1; j < nSNLen; j++) {            cl = ary256Chars[szSN[j] ^ (unsigned char)cl];        }        *(pResult + i) = g_sz26Chars[((unsigned char)cl % 0x1A)];    }}//构造一个可用的Serial Numberchar g_ary256Chars[] = { 0x23, 0xDD, 0x78... }; //这里是一个256大小的char数组void main() {    char szSN[] = { "S4SG-KRGM-YD7Q-XXXX" };    Get4charBySNPre12char(szSN, 15, g_ary256Chars, &szSN[15]);    printf("%s\r\n", szSN); //S4SG-KRGM-YD7Q-RCFY}