吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 41632|回复: 196
收起左侧

[原创] 某国外音视频转换软件之注册算法、加密算法分析及逆向注册机实现

    [复制链接]
solly 发表于 2019-12-27 17:58
本帖最后由 solly 于 2019-12-29 01:59 编辑

这篇帖子是对一个国外的音频软件的注册加密算法进行分析和逆向过程记录,分成三个部分:
1、注册码的校验码。
这是在输入用户名和注册码时进行的校验,包括注册码格式校验和CRC校验。
2、注册码加密验证。
这是一个重启后进行的验证,在软件每次启动时都会验证。(这一部分跟贴写在后面)。
3、注册算法分析和逆向注册机。
这一部分还原出软件的算法代码,再进行代码化简,分析出算法,并编写注册机。(这一部分也是分开跟帖在后面)


软件名称为 B_o_i_l_s_o_f_t  A_u_d_i_o  Converter(加点下划线脱敏,防止过敏反应),这个公司有好多个音视频相关转换、分切和合并等的工具软件,
好象注册算法和注册码格式都差不多,只是加解密的密码不一样,当然也不一定正确,因为我也没有验证其它程序的算法,只是看了一下密码。
补充:已验证该公司的视频转换软件的算法也是一样的,只是密码不一样,见后面跟贴中的已更新了的注册机的代码中的注释

一、注册码的校验码


还是按正常逆向步骤一步步来,先看看文件信息。如下图所示:
00.png

由 Microaoft VC 9.0 编译,没有加壳,这是一个好消息,不用脱壳了。
01.png
再“Scan /t"成 VC8了,没什么影响。


怎么找注册函数入口就不説了,我们先看看其导入表信息,用 Stud_PE 查看:
04.png
Stud_PE 也显示没有壳。
05.png
可以看到,这是一个基于 QT4 框架开发的软件,QT4是什么就不説了,反正就是一个 MFC 类似的开发框架,跨平台UI套件。


重点在下面:
06.png

可以看到,这里导入了一个 "trreg_bl.dll" 的库,其实,这个就是注册码算法库,注册验证的相关函数都在这里,省去大把时间找注册代码,直接在其导出函数下断就可以了,这里没有导出函数名,是用的序号。
下面用 IDA 打开这个动态库反编译,就可以看到其所有导出函数了,如下图所示:
08.png

其中所选中的那一行函数:TRRegKeyInstall_BL,就是输入并保存注册码的函数,并对注册码进行格式检查和CRC校验。


首先,我们用 OD 载入该软件,因为是QT 架构,入口代码首先一个调用进入QT进行初始化,如下图所示,已经到了QT的入口代码了:
10.png

可以看到后面有一个调用 QtCore4.qWinMain 调用,这个调用是初始化QT的环境,放大看看:
11.png
再往下走,如下图所示:
12.png
进入程序入口,我们 F7 进入此函数,可以看到如下代码,如下图所示:
13.png

这就是调用程序入口,要显示应用的UI了,我们不看这些了,先 F9 进入程序看看,如下图所示:
14.png
右上角,有一个”Register“ 按钮,这是注册用的。先看看 "About",如下所示:
15.png
显示”unregistered“,未注册。

点”Register“,进行注册,弹出如下所示注册界面:
20.png
随便输入注册假码数据,点”OK“,会弹出如下消息框:
21.png
表示注册码无效。


下面进入正题,进行注册码算法的跟踪,我们先到 trreg_bl.dll 中下断点,先找到其导出函数:
22.png
”右键“-->”查找“-->”所有模块中的名称“,显示如下界面:
23.png
找到我们需要的 "TRRegKeyInstall_BL",如上图所示,双击进入函数,如下图所示:
24.png
在函数内下一个断点,如上图所示位置。再回到软件,重新回到软件的注册界面点”OK”,马上就会来到我们下的断点处,如下图所示:
25.png
在 0x(nnnn)113E call 0x(nnnn)1FF0 处,就是注册码校验的调用。(注,nnnn 表示值不固定,dll 重定位后,高2字节可能会有变化,每次重进软件都可能不一样

按 F7 进入该函数,该函数先对用户名进行检查,检查用户名是否为空,不为空则计算用户名的长度,如下图所示:
26.png

再往下走,就是注册码的格式检查了,如下图所示:
27.png

再次按 F7 进入该函数,来到如下位置:
28.png
这里调用 call 0x(nnnn)15A0,对注册码以“-”号进行切分,分成7段,不是7段就退出了,返回到上一级函数:
29.png
这里又对注册码进行长度验证,长度应为 0x34 个字符,不是也会退出。这里説明一下,C++ 的 string 内存中的存贮格式,结构如下:
[C] 纯文本查看 复制代码
struct string {
    void * ptr;
    union {
        char * str_ptr;
        char[16] str_buff;
    }
   long length;
   long maxLength;
};

当字符串长度小于16字符时,存于结构中的 str_buff 中,当长度大于 0x0F 时,则 str_buff 前4字节保存的是字符串的地址,也就是保存的 str_ptr 指针了。
length 保存在是字符串的长度,maxLength 是可保存字符串的最大长度。
第1个成员 void * ptr 没搞清楚,一般是一个地址,不过好象也没什么用,有时为 0。


长度检查失败再往上返回,如下图所示:
30.png
当 al==0时,表示格式检查失败。
下面我们来看看,对注册码进行切分并检查的函数 call 0x(nnnn)15A0,如下图所示,我们F9返回界面,重新点“OK”再次进入注册函数,来到下面位置:
31.png
可以看到,注册码是由“-”分隔成多个部分的。按 F7 进入 call 0x(nnnn)2B90,如下图所示:
32.png

在注册码中,循环查找“-”符号,我们手动在输入的注册码“78787878”中间改一位为“-“,如上图 OD 数据区所示,跟踪代码运行情况。
具体代码如下:
[Asm] 纯文本查看 复制代码
78612BC3    8BF3                mov     esi, ebx                      ; esi ===> 注册码
78612BC5    3BF7                cmp     esi, edi
78612BC7    73 26               jnb     short 78612BEF
78612BC9    8DA424 00000000     lea     esp, dword ptr [esp]          ; 在SN中查找 "-" 字符
78612BD0    8B4424 1C           mov     eax, dword ptr [esp+1C]
78612BD4    0FBE0E              movsx   ecx, byte ptr [esi]           ; ecx == sn[i]
78612BD7    8B5424 18           mov     edx, dword ptr [esp+18]
78612BDB    50                  push    eax                           ; 1
78612BDC    51                  push    ecx                           ; sn[i] == 0x37
78612BDD    52                  push    edx                           ; '-'
78612BDE    E8 8DFE0000         call    78622A70                      ; _memchr(),检查注册码字符串是否含有"-"
78612BE3    83C4 0C             add     esp, 0C
78612BE6    85C0                test    eax, eax
78612BE8    75 0F               jnz     short 78612BF9                ; 找到"-"则跳转
78612BEA    46                  inc     esi                           ; sn++, sn[i], i++
78612BEB    3BF7                cmp     esi, edi
78612BED  ^ 72 E1               jb      short 78612BD0                ; 循环查找


如果找到”-“号,则跳转处理,如下图所示:
33.png
找到“-”号就返回其所在位置索引值,并从函数返回。
34.png
这时还是对注册码进行切分,截取”-“号后面的内容再进行检查。
35.png
如下图所示,这个 call 0x(nnnn)2B90,就是对字符串进行切分处理的函数。
36_search_2.png
在剩下的字符串,再手动改一位为”-“,验证是否会再次切分。如下图 OD 的数据区所示。
37_search_2_result.png

返回值不为-1时,表示找到”-“,继续下一轮查找和切分。如下所示:
38.png

切分完后,返回到上一级函数,如下图所示:
39.png
因为我们改了两次”-“,字符分成了 3 段,eax == 0x03 了,但还是不够7段。
我们  F9 运行,重新返回界面,输入符合要求的 7 段式注册码,如下图所示:
40.png
再次”OK“,一路来到下图所示位置:
41.png

可以看到,这次 eax == 0x00000007 了,后面的 cmp eax, 7 的检查也会成功了,分段数正确后,来到下面一个循环处:
50.png
这里对切分出来的每一段进行格式检查,前6段是一样的检查,第7段另外检查,如下图所示,检查1~6段。
51.png
来到下图所示位置,又是一个长度检查:
52_part_length_check.png
需要8位长度,上面 OD 数据区显示,我们前面输入的不对,只有5个字符长度,再次 F9 继续回到界面,输入新的注册码:
53.png
再一次”OK“,来到各段格式检查处:
54_part_length_ok.png
由上图可见,在 OD 数据区显示的字符串中,显示长度为 0x08 了,字符串格式见前面的 struct string 的説明。
长度检查完后,再进行字符范围检查,保证每段字符串都可以转化成 16 进制数,如下图所示:
55.png
上图中 call 0x(nnnn)1700 就是对字符范围进行检查。如果都没问题,则继续检查下一段:
56.png
最后,当前6段都检查完成后,就单独对第7段进行检查,如下图所示:
57.png
进入第7段检查,如下图所示:
58.png
可以看到,这里要求的长度是4个字节,我们输入的是8个字符,所以还得重新来一次,F9 后再次修改注册码:
59.png
按”OK“继续,来到前面的位置,如下图所示:
60.png

如上图所示,SN 第7段的长度为 4 了,这一次终于完美达成其对注册码分段格式的要求了。继续 F8 往下走,退出循环,来到下面位置:
61.png
这里一个调用 call 0x(nnnn)2AB0,是对刚才切分成7段的注册码重新进行连接,生成一个新字符,这个字符串就不含”-“号了,执行调用后如下图所示:
62.png

上图中的 OD 数据区显示了这个字符串的结构,因为长度为 0x34,大于 0x10,结构内只保存了字符串的地址:0x02C91F80,最大长度是 0x3F,实际长度是 0x34。
跟随上面的地址,可以看到这个字符串的内容,如下图所示:
63.png
继续 F8 向下走,来到如下图所示位置:
64.png
这里的调用是释放7个子分段的字符串了,切分及合并就完成了,退出函数,返回上一级。
65.png
这里对合并的字符串进行长度检查,长度必须为 0x34,否则就是无效的注册码。
检查长度合法后,跳转到下一步,进行注册码的有效性检查,如下图所示:
66.png
首先,调用 call 0x(nnnn)3B60 将注册码转换成 16 进制数据,如下图所示:
67.png
上图中 OD 数据区显示的是已经转换好了的数据。
接下来是对用户名的处理了,如下图所示:
70.png
通过分析,这是通过查表方式,对用户名进行 CRC 校验码的计算,如上图所示,OD 数据区是 CRC 的数据表。
对名字计算CRC后,还有一段对 NULL 字符串进行 CRC 计算的代码,猜测是该算法对机构名之类的进行 CRC 验证,但该软件没有用到。
接下来,对注册码进行奇偶的一个检查,如下图所示:
72.png
当索引号是奇数,进行累加,为偶数则进行异或,结果分别存于 BL 和 DL,是一个分开的8位校验。
接下来,把刚才计算生成2个字节,组成一个双字(word),同时取一个软件内置的常量字符串的头两个字节,对这个结果进行XOR计算,再将结果与用户名的CRC值XOR计算,如下图所示:
73.png
上面的计算结果再与前面的 CRC 值异或,结果为存于 DX,如下图所示:
74_check.png
上面生成的结果,与输入的注册码的最后2字节(SN 第7段转换后16进制数)进行比较,如下图所示:
75.png
如果相等,则表示成功,不相等则注册码无效。
按 F9 退出,返回界面修改注册码,如下图所示:
76.png
再次点”OK“,来到前面退出的位置,如下图所示:
77.png

这一次是相等的了。格式验证过程就结束了。
这个7段式注册码第7段校验码的生成和校验的具体代码如下:
[Asm] 纯文本查看 复制代码
78612067     8B4424 18              mov     eax, dword ptr [esp+18]                 ; SN, 去掉“-”的注册码
7861206B     BF 10000000            mov     edi, 10
78612070     397C24 2C              cmp     dword ptr [esp+2C], edi                 ; 检查最大长度不小于16
78612074     73 04                  jnb     short 7861207A
78612076     8D4424 18              lea     eax, dword ptr [esp+18]
7861207A     6A 1A                  push    1A                                      ; 26 个字节,长度
7861207C     8D4C24 34              lea     ecx, dword ptr [esp+34]                 ; ecx: endptr ===> null
78612080     E8 DB1A0000            call    78613B60                                ; strtol(sn, length), 将SN按字节转换成16进制字节数据
78612085     83C4 04                add     esp, 4
78612088     8BD6                   mov     edx, esi                                ; edx ===> name
7861208A     85F6                   test    esi, esi
7861208C     74 21                  je      short 786120AF
7861208E     8A0E                   mov     cl, byte ptr [esi]                      ; cl = name[i]
78612090     33C0                   xor     eax, eax                                ; long crc = 0
78612092     84C9                   test    cl, cl
78612094     74 17                  je      short 786120AD
78612096     0FB6C9                 movzx   ecx, cl                                 ; (uint)name[i]
78612099     0FB6F0                 movzx   esi, al                                 ; (uint)(crc & 0xFF)
7861209C     33CE                   xor     ecx, esi                                ; long idx = name[i] ^ (crc &0xFF)
7861209E     33048D 78346378        xor     eax, dword ptr [ecx*4+78633478]         ; crc ^= crc_table[idx]
786120A5     8A4A 01                mov     cl, byte ptr [edx+1]                    ; name[i+1]
786120A8     42                     inc     edx                                     ; name++, i++
786120A9     84C9                   test    cl, cl
786120AB   ^ 75 E9                  jnz     short 78612096
786120AD     8BF0                   mov     esi, eax                                ; eax == 0x9B64C2B0
786120AF     33C0                   xor     eax, eax
786120B1     8BD3                   mov     edx, ebx                                ; ebx == 0,  参数3
786120B3     85DB                   test    ebx, ebx
786120B5     74 20                  je      short 786120D7
786120B7     8A0B                   mov     cl, byte ptr [ebx]                      ; 另:机构名?暂无用
786120B9     84C9                   test    cl, cl
786120BB     74 1A                  je      short 786120D7
786120BD     8D49 00                lea     ecx, dword ptr [ecx]
786120C0     0FB6C9                 movzx   ecx, cl
786120C3     0FB6D8                 movzx   ebx, al
786120C6     33CB                   xor     ecx, ebx
786120C8     33048D 78346378        xor     eax, dword ptr [ecx*4+78633478]
786120CF     8A4A 01                mov     cl, byte ptr [edx+1]
786120D2     42                     inc     edx
786120D3     84C9                   test    cl, cl
786120D5   ^ 75 E9                  jnz     short 786120C0
786120D7     03F0                   add     esi, eax                                ; 两组CRC值相加
786120D9     33C0                   xor     eax, eax                                ; int i = 0
786120DB     32D2                   xor     dl, dl                                  ; char ch1 = 0
786120DD     8D4C24 30              lea     ecx, dword ptr [esp+30]                 ; ecx ===> sn_int
786120E1     32DB                   xor     bl, bl                                  ; char ch2 = 0;
786120E3     A8 01                  test    al, 1
786120E5     74 04                  je      short 786120EB
786120E7     0219                   add     bl, byte ptr [ecx]                      ; al 为奇, ch2 += sn_char[i]
786120E9     EB 02                  jmp     short 786120ED
786120EB     3211                   xor     dl, byte ptr [ecx]                      ; al 为偶,ch1 ^= sn_char[i]
786120ED     40                     inc     eax                                     ; i++
786120EE     41                     inc     ecx                                     ; sn_int ++
786120EF     83F8 18                cmp     eax, 18                                 ; 对SN前 24 个字符进行校验
786120F2   ^ 72 EF                  jb      short 786120E3
786120F4     0FB60D E7716378        movzx   ecx, byte ptr [786371E7]                ; key2[1]
786120FB     66:0FB6D2              movzx   dx, dl                                  ; ch1
786120FF     66:0FB6C3              movzx   ax, bl                                  ; ch2
78612103     66:C1E2 08             shl     dx, 8
78612107     66:0BD0                or      dx, ax                                  ; short sn_check1 = (ch1<<8) + ch2
7861210A     66:0FB605 E6716378     movzx   ax, byte ptr [786371E6]                 ; key2[0]
78612112     66:C1E1 08             shl     cx, 8
78612116     66:0BC8                or      cx, ax                                  ; short sn_check2 = (key2[1]<<8) + key2[0]
78612119     66:33D1                xor     dx, cx                                  ; short check = sn_check1 ^ sn_check2
7861211C     66:33D6                xor     dx, si                                  ; check = check ^ name_crc
7861211F     0FB7C2                 movzx   eax, dx                                 ; eax == check == 0x894D
78612122     884424 10              mov     byte ptr [esp+10], al
78612126     C1E8 08                shr     eax, 8
78612129     884424 11              mov     byte ptr [esp+11], al                   ; 转换成 short 类型
7861212D     0FB74C24 10            movzx   ecx, word ptr [esp+10]
78612132     66:3B4C24 48           cmp     cx, word ptr [esp+48]                   ; check 值与 SN 最后4位比较
78612137     74 2B                  je      short 78612164                          ; 相等则表示成功


上面对比成功后,直接 F9 运行,会弹出如下消息框:
78.png
这只是表示注册码的格式检查全部通过了,注册码正确性检查需要在重启后进行。
如果要对这个检查进行破解,进行如下操作,来到下面位置:
80_TRRegKeyInstall_BL.png
修改最上面那一条指令,如下图所示,修改后的指令为红色:
81_crack_check.png
这样就可以破解掉格式检查。
都成功后,我们退出 DLL 中的函数,来到主程序,看看是在那里调用的这个 trreg_bl 函数,如下图所示:
82.png
就是在这个位置调用的。
=================================================
太长了,注册分析和注册机,放在跟帖内写。先到这里吧。。。。

免费评分

参与人数 51吾爱币 +57 热心值 +49 收起 理由
songshao + 1 + 1 鼓励转贴优秀软件安全工具和文档!
alderaan + 1 + 1 谢谢@Thanks!
FSA1975 + 1 + 1 用心讨论,共获提升!
davidhee + 1 谢谢@Thanks!
qzh8008110 + 1 + 1 虽然我用不上,但是楼主很用心,必须赞!!!!
XingTiger + 1 + 1 用心讨论,共获提升!
Mars_Lake + 1 + 1 谢谢@Thanks!
ttao88 + 1 + 1 谢谢@Thanks!
202052pojie + 1 + 1 谢谢@Thanks!
xiefeixy + 1 + 1 谢谢@Thanks!
jinlong0186 + 1 + 1 我很赞同!
MrxWilliam + 1 + 1 兄弟牛逼,6666
hxy980703 + 1 + 1 谢谢@Thanks!
天空藍 + 1 + 1 真的好細心。
错的是世界 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yzyuan007 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
乄落日乀 + 1 + 1 用心讨论,共获提升!
菜鸟小白 + 1 + 1 谢谢@Thanks!
我为52pojie狂 + 1 + 1 谢谢@Thanks!
月六点年一倍 + 1 鼓励转贴优秀软件安全工具和文档!
撸冰花 + 1 + 1 热心回复!
bugof52pj + 1 + 1 谢谢@Thanks!
Booneey + 1 热心回复!
独行风云 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
小豆丁 + 1 + 1 我很赞同!
ofo + 3 + 1 鼓励转贴优秀软件安全工具和文档!
Bascter_Main + 1 用心讨论,共获提升!
loxohc + 1 + 1 太秀了兄弟
小小学生 + 1 + 1 太秀了哥们。赞一个
wapjphl + 1 + 1 用心讨论,共获提升!
smile1110 + 3 + 1 谢谢@Thanks!
gaosld + 1 + 1 用心讨论,共获提升!
UniqueLegend + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
无闻无问 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
朱朱你堕落了 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
dazhuangzhuang + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wu0o0pj + 1 + 1 秀!
Higher-Stark + 1 + 1 用心讨论,共获提升!
wzlyq + 1 + 1 热心回复!
zhangbaida + 3 + 1 鼓励转贴优秀软件安全工具和文档!
zhangyazhou + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
生有涯知无涯 + 1 我很赞同!
wtujoxk + 2 + 1 大神真的秀,这操作膜拜了!
iamcjsyr + 1 + 1 用心讨论,共获提升!
笙若 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lxwen + 1 + 1 热心回复!
xlnetwork + 1 热心回复!
fqr2009 + 1 + 1 谢谢@Thanks!
sighout + 1 + 1 谢谢@Thanks!
FleTime + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
GenW + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| solly 发表于 2019-12-27 17:59

三、算法逆向和注册机实现

本帖最后由 solly 于 2020-1-9 01:07 编辑

(注:跟贴顺序有时会变化,这是第三部分,如果顺序显示不对,可先看第二部分再看这一部分)
前面两部分,对加密进行了分析,这部分对注册算法进行逆向分析。因为加密共有5个过程,其中4个比较简单,很容易逆向出来,这4个就不详説了。


现在看看,最关键的一段加密,其代码如下所示:
[Asm] 纯文本查看 复制代码
786139A0     51                   push    ecx
786139A1     0FB64E 03            movzx   ecx, byte ptr [esi+3]
786139A5     0FB646 02            movzx   eax, byte ptr [esi+2]
786139A9     0FB656 01            movzx   edx, byte ptr [esi+1]
786139AD     C1E1 08              shl     ecx, 8
786139B0     0BC8                 or      ecx, eax
786139B2     0FB606               movzx   eax, byte ptr [esi]
786139B5     C1E1 08              shl     ecx, 8
786139B8     0BCA                 or      ecx, edx
786139BA     0FB656 06            movzx   edx, byte ptr [esi+6]
786139BE     C1E1 08              shl     ecx, 8
786139C1     0BC8                 or      ecx, eax                                 ; ecx == sn_part1
786139C3     0FB646 07            movzx   eax, byte ptr [esi+7]
786139C7     C1E0 08              shl     eax, 8
786139CA     0BC2                 or      eax, edx
786139CC     0FB656 05            movzx   edx, byte ptr [esi+5]
786139D0     C1E0 08              shl     eax, 8
786139D3     0BC2                 or      eax, edx
786139D5     0FB656 04            movzx   edx, byte ptr [esi+4]
786139D9     53                   push    ebx
786139DA     55                   push    ebp
786139DB     C1E0 08              shl     eax, 8
786139DE     57                   push    edi
786139DF     0BC2                 or      eax, edx                                 ; eax == sn_part2
786139E1     BF 20000000          mov     edi, 20                                  ; int n = 0x20;
786139E6     BA 2037EFC6          mov     edx, C6EF3720                            ; key = 0xC6EF3720
786139EB     4F                   dec     edi                                      ; n--
786139EC     897C24 0C            mov     dword ptr [esp+C], edi                   ; n
786139F0     8BD9                 mov     ebx, ecx                                 ; sn11 = sn_part1;
786139F2     335C24 1C            xor     ebx, dword ptr [esp+1C]                  ; sn11 = sn_part1 ^ key3;
786139F6     8BF9                 mov     edi, ecx                                 ; sn12 = sn_part1;
786139F8     035C24 20            add     ebx, dword ptr [esp+20]                  ; sn11 = sn11 + key4;
786139FC     C1EF 05              shr     edi, 5                                   ; sn12 >>= 5;
786139FF     33FA                 xor     edi, edx                                 ; sn12 ^= key;
78613A01     03DF                 add     ebx, edi                                 ; sn11 += sn12;
78613A03     8BE9                 mov     ebp, ecx                                 ; sn13 = sn_part1;
78613A05     C1E5 04              shl     ebp, 4                                   ; sn13 <<= 4;
78613A08     03EB                 add     ebp, ebx                                 ; sn13 += sn11;
78613A0A     2BC5                 sub     eax, ebp                                 ; sn_part2 -= sn13;
78613A0C     8BD8                 mov     ebx, eax
78613A0E     335C24 14            xor     ebx, dword ptr [esp+14]                  ; sn11 = sn_part2 ^ key1;
78613A12     8BF8                 mov     edi, eax                                 ; sn12 = sn_part2;
78613A14     035C24 18            add     ebx, dword ptr [esp+18]                  ; sn11 = sn11 + key2;
78613A18     C1EF 05              shr     edi, 5                                   ; sn12 >>= 5;
78613A1B     33FA                 xor     edi, edx                                 ; sn12 ^= key;
78613A1D     8BE8                 mov     ebp, eax                                 ; sn13 = sn_part2;
78613A1F     03DF                 add     ebx, edi                                 ; sn11 += sn12;
78613A21     8B7C24 0C            mov     edi, dword ptr [esp+C]                   ; n
78613A25     C1E5 04              shl     ebp, 4                                   ; sn13 <<= 4;
78613A28     03EB                 add     ebp, ebx                                 ; sn13 += sn11;
78613A2A     2BCD                 sub     ecx, ebp                                 ; sn_part1 -= sn13;
78613A2C     81C2 4786C861        add     edx, 61C88647                            ; key += 0x61C88647;
78613A32     85FF                 test    edi, edi
78613A34   ^ 77 B5                ja      short 786139EB                           ; 循环执行32次
78613A36     33FF                 xor     edi, edi                                 ; 0
78613A38     0BC7                 or      eax, edi                                 ; sn_part2 = 0x4C22825B
78613A3A     33D2                 xor     edx, edx                                 ; 0
78613A3C     0BD1                 or      edx, ecx                                 ; sn_part1 = 0x4F16BACE
78613A3E     8BCA                 mov     ecx, edx                                 ; sn_part1 = 0x4F16BACE
78613A40     8BF8                 mov     edi, eax                                 ; sn_part2 = 0x4C22825B
78613A42     0FACF9 08            shrd    ecx, edi, 8                              ; save to sn_int, edi最低8bits移到ecx的最高8bits
78613A46     884E 01              mov     byte ptr [esi+1], cl                     ; save
78613A49     C1EF 08              shr     edi, 8
78613A4C     8BCA                 mov     ecx, edx
78613A4E     8BF8                 mov     edi, eax
78613A50     0FACF9 10            shrd    ecx, edi, 10
78613A54     884E 02              mov     byte ptr [esi+2], cl                     ; save
78613A57     8816                 mov     byte ptr [esi], dl                       ; save
78613A59     8BC8                 mov     ecx, eax
78613A5B     0FACCA 18            shrd    edx, ecx, 18
78613A5F     C1E9 18              shr     ecx, 18
78613A62     8856 03              mov     byte ptr [esi+3], dl                     ; save
78613A65     8BD0                 mov     edx, eax
78613A67     C1EF 10              shr     edi, 10
78613A6A     8BC8                 mov     ecx, eax
78613A6C     5F                   pop     edi
78613A6D     8856 04              mov     byte ptr [esi+4], dl                     ; save
78613A70     C1E9 08              shr     ecx, 8
78613A73     C1EA 10              shr     edx, 10
78613A76     C1E8 18              shr     eax, 18
78613A79     5D                   pop     ebp
78613A7A     884E 05              mov     byte ptr [esi+5], cl                     ; save
78613A7D     8856 06              mov     byte ptr [esi+6], dl                     ; save
78613A80     8846 07              mov     byte ptr [esi+7], al                     ; save
78613A83     5B                   pop     ebx
78613A84     59                   pop     ecx
78613A85     C3                   retn

先转换成 C 代码:
[C++] 纯文本查看 复制代码
//// Keys0 = {0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A}
long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
        ULONG sn11, sn12, sn13;
        //// 取注册码 
        ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
        ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
        
        ULONG key = 0xC6EF3720;
        
        for(int i=0; i<32; i++) {
                sn11 = sn_part1 ^ key3;
                sn11 += key4;
                
                sn12 = sn_part1 >> 5;
                sn12 ^= key;
                
                sn11 += sn12;
                
                sn13 = sn_part1 << 4;
                sn13 += sn11;
                
                sn_part2 -= sn13;
                
                /////
                sn11 = sn_part2 ^ key1;
                sn11 += key2;
                
                sn12 = sn_part2 >> 5;
                sn12 ^= key;
                
                sn11 += sn12;
                
                sn13 = sn_part2 << 4;
                sn13 += sn11;
                
                sn_part1 -= sn13;
                
                key += 0x61C88647;
        }
        //// sn_part1= 0x4F16BACE, sn_part2 = 4C22825B, key = 0x00000000
        *(ULONG *)&sn_int[0] = sn_part1;
        *(ULONG *)&sn_int[4] = sn_part2;
        printSN(6); 
        
        return 0;
}



对代码进行优化一下,如下所示:
[C++] 纯文本查看 复制代码
//// Keys0 = {0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A}
long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
        ULONG sn11, sn12, sn13;
        //// 取注册码
        ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
        ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
        
        ULONG key = 0xC6EF3720;
        
        for(int i=0; i<32; i++) {
                sn11 = (sn_part1 ^ key3) + key4;
                sn12 = (sn_part1 >> 5) ^ key;
                sn13 = (sn_part1 << 4);

                sn_part2 -= (sn11 + sn12 + sn13);
                
                /////
                sn11 = (sn_part2 ^ key1) + key2;
                sn12 = (sn_part2 >> 5) ^ key;
                sn13 = (sn_part2 << 4);
                
                sn_part1 -= (sn11 + sn12 + sn13);
                
                key += 0x61C88647;
        }

        *(ULONG *)&sn_int[0] = sn_part1;
        *(ULONG *)&sn_int[4] = sn_part2;
        printSN(3); 
        
        return 0;
}



可以看到,是用一个64位密码(两组32位),再加另一个32位key(计算的密码,每轮都不一样)(其实是IV),分别对一段SN(32位数据)加密,同时,SN两段数据还交叉进行加密。
一眼也看不出是什么加密方法,上网查资料,看看哪个算法有32轮加密,直到看到一个 DES 加密的框图,如下所示:
DES_Encrypt.jpg
可以看到,这里也会将64位明文,分成两个32位交叉加密,是不是很象前面的加密代码,不过 DES 一般只进行 16 轮加密,而这里用了 32 轮,比标准的多一倍,并且也没有对明文和密文进行置换,因此,对 DES 加密过程也进行了简化。
因此,可以看成是对标准 DES 算法的一种变形,那解密算法自然也是 DES 解密算法的变形,经过对标准 DES 算法的基本了解和查阅了一些资料,终于逆向出解密算法如下:
[Asm] 纯文本查看 复制代码
//// step2_2() like DES_Decrypt()
int step2_2(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
        ULONG sn11, sn12, sn13;
        //// 取注册码 
        ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
        ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
        
        ULONG IV = 0x00000000; //init IV

        for(int i=0; i<32; i++) {

                IV -= 0x61C88647;
                
                /////
                sn11 = (sn_part2 ^ key1) + key2;
                sn12 = (sn_part2 >> 5) ^ IV;
                sn13 = (sn_part2 << 4);

                sn_part1 += (sn11 + sn12 + sn13);
                
                /////
                sn11 = (sn_part1 ^ key3) + key4;
                sn12 = (sn_part1 >> 5) ^ IV;
                sn13 = (sn_part1 << 4);
                
                sn_part2 += (sn11 + sn12 + sn13);
        }

        *(ULONG *)&sn_int[0] = sn_part1;
        *(ULONG *)&sn_int[4] = sn_part2;
        //printSN(3); 
        
        return 0;
}

与加密算法很相似,只是顺序反过来,包括那个计算的密码也是顺序反过来。
完整的注册机代码如下,包括了注册码的加密过程(从输入的假码到完成加密)和逆向解密过程(在加密后的假码中的将正确的用户名CRC值替换原值,并解密返回到可用的注册码SN 0),便于了解SN在加密、解密过程的变化:
[C++] 纯文本查看 复制代码
#include <iostream>
#include "crc_table.h"

typedef unsigned char  UCHAR;
typedef unsigned short UWORD;
typedef unsigned long  ULONG;
typedef unsigned long long UINT64;

/*
// Video Joiner 
UCHAR KEY0[] = {0xC6, 0x99};
UCHAR KEY1[] = {0xB2, 0xB5, 0xA9, 0xB7, 0x97, 0xA3, 0xD0, 0xE6, 
                0x9D, 0x8B, 0xA1, 0xD2, 0x63, 0xB7, 0x96, 0xDD};
char KEY2[] = "Q2JmTzS8fIKsNQBI8itv01Yir6Is4846";
char KEY3[] = "ncYEVD34az43";   
*/

/*
// Video Splitter
UCHAR KEY0[] = {0x6A, 0xA2};
UCHAR KEY1[] = {0x99, 0xA8, 0x9B, 0x8E, 0x89, 0xD8, 0x7E, 0x93, 
                0x9E, 0xC0, 0xE1, 0x78, 0x8E, 0x7D, 0xA8, 0xC9};
char KEY2[] = "0HCw296HPiAWxg9v5lGQ4rA13Ejg52I8";
char KEY3[] = "Q7H4I53CngYt";   
*/

/*
/// Video Converter
UCHAR KEY0[] = {0x68, 0x8B};
UCHAR KEY1[] = {0x96, 0xD4, 0x67, 0xAE, 0x8E, 0x82, 0x7A, 0xAB,
                0x84, 0xDD, 0xBC, 0xEB, 0x90, 0x75, 0xCA, 0x6C};
char KEY2[] = "11s8hYyw9QW74U1Gw5893241o65rncsW";
char KEY3[] = "841a036f5vDr";   
*/

///*
/// Audio Converter
UCHAR KEY0[] = {0xBD, 0x88};
UCHAR KEY1[] = {0xAD, 0xD9, 0xDF, 0xA4, 0x9E, 0xB1, 0xA2, 0x9A, 
                0x86, 0x9A, 0x8F, 0xC0, 0x6A, 0xB6, 0xA4, 0x9B};
char KEY2[] = "W9K85WVnk9BlCqM8f43rSwZ6T3748b44";
char KEY3[] = "7Kb3xboI268I";  
//*/

//unsigned long KEY4=0;
//unsigned long KEY5=0;
//unsigned long KEY6=0;

void printSN(int theTimes);

long getCRC(char * buff);
unsigned short getSNCheck(char * sn_char);
long getKeyCheck1(char * key, char * sn_int);
long getKeyCheck2(unsigned long key, char * keyStr);

long getKey2Check1(char * key, char * sn_int);
long getKey2Check2(unsigned long key, char * sn_int);

long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int);

long getSNCheck2(char * sn_int);

long getNameCheck(char * name);

int keyTransform1(char * keyStr, ULONG * key_trans);

//// application algorithm
int getAlgorithm(char * name);

//// calculate serial number
int getSN(char * name); 

//// test sn: 11111111-22222222-33333333-44444444-55555555-66666666-7777 
char sn_char_test[] = {0x11, 0x11, 0x11, 0x11, 
                       0x22, 0x22, 0x22, 0x22, 
                       0x33, 0x33, 0x33, 0x33, 
                       0x44, 0x44, 0x44, 0x44, 
                       0x55, 0x55, 0x55, 0x55, 
                       0x66, 0x66, 0x66, 0x66, 
                       0x77, 0x77}; ////check
//char sn_char_test[] = {0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52, 0x52, 0x52, 
//                       0x52, 0x52}; ////check

int main(int argc, char** argv) {
    
    char name[] = "solly"; /// modify name here
    //char name[] = "52pojie.cn"; /// modify name here
    
    printf("User Name: %s\n", name);

    ///// calculate
    printf("\n============================ start calculate =============================\n");
    ////
    int a = getAlgorithm(name);
    
    //// get successfal flag value
    printf("\n============================ show check value ============================\n");
    long name_check = getNameCheck(name);
    
    printf("Name check: 0x%08X\n", name_check);
    
    UWORD key = (UWORD) name_check;
    UWORD sn_key = * (UWORD *)&sn_char_test[18];  /// sn_int[19]sn_int[18]
    
    printf("key valid flag: 0x%04X, key calculated flag: 0x%04X\n", key, sn_key); 
    
    ///// reverse
    printf("\n======================== start reverse calculate =========================\n");
    ////
    int b = getSN(name);

    return 0;
}

void printSN(int theTimes) {
    int n = 26; 
    printf("SN %d:  ", theTimes);
    for(int i=0; i<n; i++) {
        if((i>0) && (i % 4 == 0)) {
            printf("-");
        }
        printf("%02X", (unsigned char)sn_char_test[ i ]);
    }
    printf("\n");
}

//// registration algorithm
int getAlgorithm(char * name) {
    ////
    printSN(0); /// 显示注册码 
    ////
    long nameCRC = getCRC(name);
    ///printf("Name check: 0x%08X\n", nameCRC);

    long organCRC = getCRC(NULL); //// 目前没有用到,为空 

    short snCHK = getSNCheck(sn_char_test);

    unsigned short sn_check = (short)(nameCRC + organCRC) ^ snCHK;
    
    ///printf(" SN CHECK: 0x%04X\n", sn_check);

    sn_char_test[24] = (unsigned char)(sn_check);
    sn_char_test[25] = (unsigned char)(sn_check>>8);
    
    //// 下面进行注册码处理 
    
    printSN(1); /// 显示注册码 

    long keyCheck = getKeyCheck1(KEY2, sn_char_test);
    
    printSN(2); /// 显示注册码 

    getKey2Check1(KEY3, sn_char_test);
    
    //printSN(3); /// 显示注册码 
    
    ////
    long sn_check2 = getSNCheck2(sn_char_test);

    printSN(5); /// 显示注册码 
    
    return 0;    
} 

long getCRC(char * buff) {
    long crc = 0;
    if(buff == NULL) {
        return 0;
    }
    while(*buff != '\0') {
        crc ^= crc_table[(unsigned int)((*buff) ^ (unsigned char)crc)];
        buff++;
    }
    
    return crc;
}

unsigned short getSNCheck(char * sn_char) {
    unsigned char ch_even = 0;
    unsigned char ch_odd  = 0;
    unsigned short sn_check = 0;
    
    if(sn_char == NULL) {
        return 0;
    }

    for(int i=0; i<24; i++) {
        if(i % 2) {
            ch_odd += sn_char[ i ];
        } else {
            ch_even ^= sn_char[ i ];
        }
    }

    sn_check = (ch_even<<8) + ch_odd;
    sn_check ^= ((UWORD)KEY3[1]<<8) + (UWORD)KEY3[0];
    
    return sn_check;
}

/// Key1: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
/// return: 
/// chk1: 0xE88F784B
/// chk2: 0x7319B02E
long getKeyCheck2(unsigned long key, char * keyStr) {
    unsigned char ch = 0;
    unsigned long ch1 = 0;
    unsigned long ch2 = key;

    do {
        ch = (unsigned char)(* keyStr);
        ch1 = ((unsigned long)ch << 8);
        ch2 ^= ch1;
    
        unsigned short i = 0;
        do {
            ch1 = (unsigned char)i ^ ch2;
            ch1 += 0x7034616B; //// "ka4k"
            ch2 = (unsigned char)ch1;
            ch2 &= 0x1F;
            if(ch2 != 0) {
                ch1 = (ch1 >> ch2) + (ch1<<(32-ch2)); /// ROR
            }
            ch2 = ch1 ^ 0x8372A5A7;
            i--; //i += 0xFFFF;
        } while(i);
    
        keyStr ++;
    } while(ch);
    
    return ch2;
}

/// 将SN的6段分别与6个常量进行xor操作 
/// Key: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
long getKeyCheck1(char * key, char * sn_int) {
    unsigned long ch1 = 0;
    unsigned long chk = 0;
    /// chk1 and chk1 calculated from Key: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44" 
    //unsigned long chk1[] = {0xE88F784B, 0x20E57D8E, 0xA522A5A6, 0x2C245DBC, 0x712E29E9, 0xB2BAF74F};
    //unsigned long chk2[] = {0x7319B02E, 0x42D2836B, 0x7936349F, 0xD9930AE9, 0x2872B191, 0x5EE814F3};
    unsigned long chk2[6];
    keyTransform1(key, chk2);
    
    //// 以整数方式存取 
    unsigned long * sn = (unsigned long *)&sn_int[0]; 
    
    for(int i=0; i<6; i++) {
        sn[ i ] = sn[ i ]  ^ chk2[ i ];
    }
    
    return 0;//chk1;
}

long getKey2Check1(char * key, char * sn_int) {
    unsigned long ch = 0;
    unsigned long ch1 = 0;
    unsigned long ch2 = 0;
    unsigned long ch3 = 0;
    unsigned long ch4 = 0;
    unsigned long ch5 = 0;
    unsigned long ch6 = 0;
    
    //// 由key2组合成两个整数 
//  //// 两整数相加 
    //// char key2[] = "7Kb3xboI268I";  //// 0x33624B37, 0x496F6278, 0x49383632 
//    ch1 = 0x496F6278; // (7-6-5-4)
//    ch3 = 0x49383632; // (11-10-9-8)
    ch1 = *(ULONG *)&KEY3[4]; // (7-6-5-4)
    ch3 = *(ULONG *)&KEY3[8]; // (11-10-9-8)
    ch = ch1 + ch3; //// ch = 0x92A798AA
    long check2 = getKey2Check2(ch, sn_int);
    
    printSN(3); 
    
    //// 由 key0 组合成 4 个整数
    //unsigned long key0[] = {0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A};
//    ch4 = 0x9BA4B66A; //(17-16-15-14) 
//    ch3 = 0xC08F9A86; //(13-12-11-10)
//    ch2 = 0x9AA2B19E; //(9-8-7-6)
//    ch1 = 0xA4DFD9AD; //(5-4-3-2)
    ch4 = *(ULONG *)&KEY1[12]; //(17-16-15-14) 
    ch3 = *(ULONG *)&KEY1[8]; //(13-12-11-10)
    ch2 = *(ULONG *)&KEY1[4]; //(9-8-7-6)
    ch1 = *(ULONG *)&KEY1[0]; //(5-4-3-2)
    //// 每次同时处理两段SN 
    unsigned long long * sn_llu = (unsigned long long *)(& sn_int[0]);  
    for(int i=0; i<3; i++) {
        getKey0Check(ch1, ch2, ch3, ch4, (char *)(& sn_llu[ i ]));
    }
    
    printSN(4); 
    
    return 0;
}

ULONG htonl(ULONG l) {
    ULONG tmp = l;
    ULONG nl = (UCHAR)tmp;
    nl <<= 8;
    nl += (UCHAR)(tmp >> 8);
    nl <<= 8;
    nl += (UCHAR)(tmp >> 16);
    nl <<= 8;
    nl += (UCHAR)(tmp >> 24);
    
    return nl;
}

////
// ch1 = 0x496F6278; // key2[](7-6-5-4)
// ch2 = 0x49383632; // key2[](11-10-9-8)
// key = ch1 + ch2; //// ch = 0x92A798AA
long getKey2Check2(unsigned long key, char * sn_int) {
    unsigned short key0 = 0;
    unsigned short key1 = 0;
    unsigned long  key2 = 0;
    unsigned long  key3 = 0;
    unsigned long  ch = 0;
    unsigned long  sn_part = 0;
    
    int n = 6;
    unsigned long sn_origin = 0;
    unsigned long sn_new = 0;
    unsigned long sn_tmp = 0;
    
    ////printf("    key: 0x%08X\n", key);
    
    //unsigned char * sn_ptr = (unsigned char *)&sn_int[2];
    ULONG * sn = (ULONG *)(& sn_int[0]);
    
    for(int i=0; i<n; i++) {
        key -= 0x76BDEFDB;
        //// key 字节顺序置换 
        key = htonl(key); //// 调整字节顺序 
        //printf("new key: 0x%08X\n", key);
    
        //// 将SN每部分4字节组合成一个整数
        sn_part = sn[ i ];
        sn_tmp = sn_part; //// save temp 
        //printf("sn_Part1: 0x%08X\n", sn_part);
    
        //// 计算 
        sn_part -= key;
        sn_part ^= sn_origin;
        //printf("sn_Part2: 0x%08X\n", sn_part);
    
        //// 保存 
        sn[ i ] = sn_part;
    
        //sn_ptr += 4;  ///// SN 下一段
        //n--;
        sn_origin = sn_tmp;  /// SN 上一段的值,作为下一段的异常参数
    }// while(n>0); 
    
    return 0;
}

//// Keys0 = {0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A}
long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
    ULONG sn11, sn12, sn13;
    //// 取注册码 
    ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
    ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
    
    ULONG key = 0xC6EF3720;
/* 32 个 key: key[ i ] = key[i-1] + 0x61C88647 
0xC6EF3720, 0x28B7BD67, 0x8A8043AE, 0xEC48C9F5, 
0x4E11503C, 0xAFD9D683, 0x11A25CCA, 0x736AE311, 
0xD5336958, 0x36FBEF9F, 0x98C475E6, 0xFA8CFC2D, 
0x5C558274, 0xBE1E08BB, 0x1FE68F02, 0x81AF1549, 
0xE3779B90, 0x454021D7, 0xA708A81E, 0x08D12E65,
0x6A99B4AC, 0xCC623AF3, 0x2E2AC13A, 0x8FF34781, 
0xF1BBCDC8, 0x5384540F, 0xB54CDA56, 0x1715609D, 
0x78DDE6E4, 0xDAA66D2B, 0x3C6EF372, 0x9E3779B9//, 0x00000000 
*/
    //printf("key: 0x%08X, sn1: 0x%08X, sn2: 0x%08X\n", key, sn_part1, sn_part2);
    //printf("key0key: \n");
    for(int i=0; i<32; i++) {
        sn11 = sn_part1 ^ key3;
        sn11 += key4;
        
        sn12 = sn_part1 >> 5;
        sn12 ^= key;
        
        sn11 += sn12;
        
        sn13 = sn_part1 << 4;
        sn13 += sn11;
        
        sn_part2 -= sn13;
        
        /////
        sn11 = sn_part2 ^ key1;
        sn11 += key2;
        
        sn12 = sn_part2 >> 5;
        sn12 ^= key;
        
        sn11 += sn12;
        
        sn13 = sn_part2 << 4;
        sn13 += sn11;
        
        sn_part1 -= sn13;
        
        key += 0x61C88647;
        //printf("key: 0x%08X, sn1: 0x%08X, sn2: 0x%08X\n", key, sn_part1, sn_part2);
    }
    //printf("\nkey0key: 0x%08X\n", key); /// 最后 key == 0x00000000 
    //// sn_part1= 0x4F16BACE, sn_part2 = 0x4C22825B, key = 0x00000000
    *(ULONG *)&sn_int[0] = sn_part1;
    *(ULONG *)&sn_int[4] = sn_part2;
    //printSN(3); 
    
    return 0;
}

long getSNCheck2(char * sn_int) {
    char low = sn_int[22]; /// 0x4D
    char up  = sn_int[23]; /// 0x5F
    
    //printf("up = 0x%02X, low = 0x%02X\n", up, low);
    
    for(int i=0; i<22; i++) {
        sn_int[ i ] = (sn_int[ i ] - up) ^ low;
    }
    
    return 0;//(up<<8) + low;
}

long getNameCheck(char * name) {
    ULONG check = 0;
    while(*name != '\0') {
        check = check ^ crc_table[(UCHAR)check ^ (UCHAR)(* name)];
        name ++;
    }
    
    return check;
}

/////
/*
SN CRC: 0x894D
SN 0:  11111111  22222222  33333333  44444444  55555555  66666666  7777
SN 1:  11111111  22222222  33333333  44444444  55555555  66666666  4D89
SN 2:  3FA10862  49A1F060  AC07054A  AD4ED79D  C4E4277D  95728E38  4D89
SN 3:  CEBA164F  5B82224C  AB6AFAAC  0A1589E4  E1190CCD  2407D7E7  4D89
SN 3:  CEBA164F  5B82224C  4B43745D  754702AF  E1190CCD  2407D7E7  4D89
SN 3:  CEBA164F  5B82224C  4B43745D  754702AF  F3D8A75B  FE0A4D5F  4D89
SN 3:  CEBA164F  5B82224C  4B43745D  754702AF  F3D8A75B  FE0A4D5F  4D89
SN 4:  2216FABD  B16E8EA0  A1A958B3  5BA5EE1D  D93405B1  D2E64D5F  4D89
Name check: 0x9B64C2B0
key ok: 0xC2B0, key_act: 0xB105
*/ 

//// ==================== start reverse ===========================

//// ready
int step0(char * name, char * sn_int) {
    ULONG name_check = (ULONG)getNameCheck(name);
    
    //// update name_check to valid value
    * (UWORD *)&sn_int[18] = (UWORD)name_check;  /// sn_int[19]sn_int[18]
    
    return name_check;
}

int step1(char * sn_int) {
    char low = sn_int[22]; /// 0x4D
    char hi  = sn_int[23]; /// 0x5F
    
    for(int i=0; i<22; i++) {
        sn_int[ i ] = (sn_int[ i ] ^ low) + hi;
    }
    
    return 0;          
}

// ch1 = 0x496F6278; // key2[](7-6-5-4)
// ch2 = 0x49383632; // key2[](11-10-9-8)
// key = ch1 + ch2; //// ch = 0x92A798AA
// like RC4_Decrypt
int step2_1(ULONG key, char * sn_int) {
    
    ULONG * sn = (ULONG *)(& sn_int[0]);
    
    ULONG sn_origin = 0;
    for(int i=0; i<6; i++) {
        key -= 0x76BDEFDB;
        //// key 字节顺序置换 
        key = htonl(key); //// 调整字节顺序 

        //// descrypt
        sn[ i ] = (sn[ i ] ^ sn_origin) + key;
    
        sn_origin = sn[ i ];  /// SN 上一段的值,作为下一段的异常参数
    }
    
    return 0;
}

//// step2_2() like DES_Decrypt()
int step2_2(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
    ULONG sn11, sn12, sn13;
    //// 取注册码 
    ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
    ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
    
    ULONG IV = 0x00000000; //init IV

    for(int i=0; i<32; i++) {

        IV -= 0x61C88647;
        
        /////
        sn11 = (sn_part2 ^ key1) + key2;
        sn12 = (sn_part2 >> 5) ^ IV;
        sn13 = (sn_part2 << 4);

        sn_part1 += (sn11 + sn12 + sn13);
        
        /////
        sn11 = (sn_part1 ^ key3) + key4;
        sn12 = (sn_part1 >> 5) ^ IV;
        sn13 = (sn_part1 << 4);
        
        sn_part2 += (sn11 + sn12 + sn13);
    }

    *(ULONG *)&sn_int[0] = sn_part1;
    *(ULONG *)&sn_int[4] = sn_part2;
    //printSN(3); 
    
    return 0;
}

///  
int step2(char * sn_int) {
//    ULONG key4 = 0x9BA4B66A; //(17-16-15-14) 
//    ULONG key3 = 0xC08F9A86; //(13-12-11-10)
//    ULONG key2 = 0x9AA2B19E; //(9-8-7-6)
//    ULONG key1 = 0xA4DFD9AD; //(5-4-3-2)
    ULONG key4 = * (ULONG *)&KEY1[12]; 
    ULONG key3 = * (ULONG *)&KEY1[8]; 
    ULONG key2 = * (ULONG *)&KEY1[4]; 
    ULONG key1 = * (ULONG *)&KEY1[0]; 
    //// 64 bits data from sn
    UINT64 * sn_llu = (UINT64 *)(& sn_int[0]);  
    for(int i=0; i<3; i++) {
        step2_2(key1, key2, key3, key4, (char *)(& sn_llu[ i ]));
    }
    printSN(3); 
    
    //// char key2[] = "7Kb3xboI268I";  //// 0x33624B37, 0x496F6278, 0x49383632 
//    ULONG key5 = 0x496F6278; // (7-6-5-4)
//    ULONG key6 = 0x49383632; // (11-10-9-8)
    ULONG key5 = * (ULONG *)&KEY3[4]; // (7-6-5-4)
    ULONG key6 = * (ULONG *)&KEY3[8]; // (11-10-9-8)
    ULONG key = key5 + key6; //// key = 0x92A798AA
    long check2 = step2_1(key, sn_int);
    printSN(2); 
    
    return 0;
}

//// 1: 0xE88F784B, 2: 0x7319B02E
int keyTransform2(ULONG key, char * keyStr) {
    unsigned char ch = 0;
    unsigned long ch1 = 0;
    unsigned long ch2 = key;

    do {
        ch = (unsigned char)(* keyStr);
        ch1 = ((unsigned long)ch << 8);
        ch2 ^= ch1;
    
        unsigned short i = 0;
        do {
            ch1 = (unsigned char)i ^ ch2;
            ch1 += 0x7034616B;
            ch2 = (unsigned char)ch1;
            ch2 &= 0x1F;
            if(ch2 != 0) {
                ch1 = (ch1 >> ch2) + (ch1<<(32-ch2)); /// ROR
            }
            ch2 = ch1 ^ 0x8372A5A7;
            i--; //i += 0xFFFF;
        } while(i);
    
        keyStr ++;
    } while(ch);
    
    return ch2;
}


//// KeyStr: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
int keyTransform1(char * keyStr, ULONG * key_trans) {
    const ULONG k = 0x41363233;
    
    if((keyStr == NULL) || (key_trans == NULL)) {
        return -1;
    }
    
    ULONG ch1 = (ULONG)keyStr[0];
    ULONG ch2 = (ULONG)keyStr[0];
    
    ch1 ^= k;
    ULONG chk1 = keyTransform2(ch1, keyStr); //// key 变换 
    //printf("Key trans chk1: 0x%08X\n", chk1);
    
    ///
    ch2 = (ch2<<8) ^ chk1;
    ULONG chk2 = keyTransform2(ch2, keyStr); //// key 变换 
    //printf("Key trans chk2: 0x%08X\n", chk2);
    
    for(int i=0; i<6; i++) {
        key_trans[i] = chk2;
        //printf("Key trans %d: 0x%08X\n", i, key_trans[i]);

        ULONG n1 = chk2 & 0x1F;
        if(n1 != 0) {
            chk1 = (chk1<<n1) + (chk1>>(32-n1));  /// ROL 
        }
        ULONG n2 = ((chk1 >> 8) & 0x1F);
        chk2 = chk1 ^ chk2;
        if(n2 != 0) {
            chk2 = (chk2 >> n2) + (chk2 << (32-n2)); /// ROR
        }
        chk1 += chk2;
        ////
    }
    
    return chk2;
}

/// 将SN的6段分别与6个常量进行xor操作 
/// Key: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
int step3(char * key, char * sn_int) {
    /// key transformed from: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44" 
    //ULONG key1[] = {0xE88F784B, 0x20E57D8E, 0xA522A5A6, 0x2C245DBC, 0x712E29E9, 0xB2BAF74F};
    ULONG key_trans[6];// = {0x7319B02E, 0x42D2836B, 0x7936349F, 0xD9930AE9, 0x2872B191, 0x5EE814F3};
    keyTransform1(key, key_trans);
    //// 以整数方式存取
    ULONG * sn = (ULONG *)&sn_int[0]; 
    
    for(int i=0; i<6; i++) {
        sn[ i ] = sn[ i ]  ^ key_trans[ i ];
    }
    
    return 0;//chk1;
}

int step4(char * name, char * sn_int) {
    long nameCRC = getCRC(name);
    long organCRC = getCRC(NULL); //// 目前没有用到,为空 
    short snCHK = getSNCheck(sn_char_test);
    
    UWORD sn_check = (short)(nameCRC + organCRC) ^ snCHK;
    
    //// update sn_check to valid value
    * (UWORD *)&sn_int[24] = sn_check;  /// sn_int[25]sn_int[24]
    
    return sn_check;
}

int getSN(char * name) {

    step0(name, sn_char_test);    
    
    printSN(5);

    step1(sn_char_test);
    
    printSN(4);
    
    step2(sn_char_test);  /// it has two steps

    //printSN(2);
    
    step3(KEY2, sn_char_test); 

    printSN(1);
    
    step4(name, sn_char_test);    

    //// final sn is valid
    printSN(0);
    
    return 0;    
}


另外,CRC 查表数据头文件:crc_table.h 如下:
[C++] 纯文本查看 复制代码
//// crc计算的查表数据
#ifndef __crc_table_h__
#define __crc_table_h__

long crc_table[] = {
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
#endif


计算结果如下:
[Shell] 纯文本查看 复制代码
User Name: solly

============================ start calculate =============================
SN 0:  11111111-22222222-33333333-44444444-55555555-66666666-7777
SN 1:  11111111-22222222-33333333-44444444-55555555-66666666-4D89
SN 2:  3FA10862-49A1F060-AC07054A-AD4ED79D-C4E4277D-95728E38-4D89
SN 3:  24B85F92-CE17FE7D-AB6AFAAC-0A1589E4-E1190CCD-2407D7E7-4D89
SN 4:  CEBA164F-5B82224C-4B43745D-754702AF-F3D8A75B-FE0A4D5F-4D89
SN 5:  2216FABD-B16E8EA0-A1A958B3-5BA5EE1D-D93405B1-D2E64D5F-4D89

============================ show check value ============================
Name check: 0x9B64C2B0
key valid flag: 0xC2B0, key calculated flag: 0xB105

======================== start reverse calculate =========================
SN 5:  2216FABD-B16E8EA0-A1A958B3-5BA5EE1D-D934B0C2-D2E64D5F-4D89
SN 4:  CEBA164F-5B82224C-4B43745D-754702AF-F3D85CEE-FE0A4D5F-4D89
SN 3:  24B85F92-CE17FE7D-AB6AFAAC-0A1589E4-BB21717E-494633AD-4D89
SN 2:  3FA10862-49A1F060-AC07054A-AD4ED79D-8EFCF20F-7C495F40-4D89
SN 1:  11111111-22222222-33333333-44444444-1F4D8027-8F5DB71E-4D89
SN 0:  11111111-22222222-33333333-44444444-1F4D8027-8F5DB71E-C42E

--------------------------------
Process exited after 0.1549 seconds with return value 0
请按任意键继续. . .


再来一个:
[Shell] 纯文本查看 复制代码
User Name: 52pojie.cn

============================ start calculate =============================
SN 0:  52525252-52525252-52525252-52525252-52525252-52525252-5252
SN 1:  52525252-52525252-52525252-52525252-52525252-52525252-BDB9
SN 2:  7CE24B21-39D18010-CD66642B-BB58C18B-C3E3207A-A146BA0C-BDB9
SN 3:  61F9A251-9D04CDEE-3AFAEABD-797A12B7-F00E15C6-2F543C15-BDB9
SN 4:  B0A54A27-886F3D22-2E38ED99-1AD46F14-FE96EECF-B184EE74-BDB9
SN 5:  D2DF385D-FA152740-542A97CB-488E154E-64CC94B5-D3FEEE74-BDB9

============================ show check value ============================
Name check: 0x4FDFF252
key valid flag: 0xF252, key calculated flag: 0xB594

======================== start reverse calculate =========================
SN 5:  D2DF385D-FA152740-542A97CB-488E154E-64CC52F2-D3FEEE74-BDB9
SN 4:  B0A54A27-886F3D22-2E38ED99-1AD46F14-FE963090-B184EE74-BDB9
SN 3:  61F9A251-9D04CDEE-3AFAEABD-797A12B7-FBD3B937-99CCFAE2-BDB9
SN 2:  7CE24B21-39D18010-CD66642B-BB58C18B-B818C5E8-D662DDA7-BDB9
SN 1:  52525252-52525252-52525252-52525252-29A9B7C0-257635F9-BDB9
SN 0:  52525252-52525252-52525252-52525252-29A9B7C0-257635F9-0D37

--------------------------------
Process exited after 0.1664 seconds with return value 0
请按任意键继续. . .


上面两个例子最后输出的 SN 0 就是正确的注册码了。
可以看到,注册码的前4段没什么用,只影响最后的 CRC 校验值。
分析完毕!!!!


免费评分

参与人数 4吾爱币 +4 热心值 +2 收起 理由
erices + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
perfectjay + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
blockke + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
netCheney + 1 + 1 用心讨论,共获提升!

查看全部评分

 楼主| solly 发表于 2019-12-27 17:58

二、注册算法分析

本帖最后由 solly 于 2020-1-9 01:12 编辑

注:跟贴顺序有时会变化,这是第二部分
前面写的是注册码格式的校验分析,下面进行注册码有效性校验分析,这个校验是软件启动时进行的,也就是重启校验。


我们用 OD 重新载入软件,F9 运行,OD 直接断了下来,原来我们在格式检查时下的断点还有效,因为重启后还进行了格式检查,我们退出格式检查函数,来到下图所示位置:
10.png
上图中 call 0x(nnnn)1FF0就检查注册码格式并保存注册码的调用,我们就是断在了这个函数里面,现在我们禁用所有前面在函数内下的断点,如上图所示,在调用外下一个新断点即可。
最下面的 call 0x(nnnn)1770也是格式检查。


按 F8 往下执行,如下图所示,对注册码字符串进行格式转换,前面已经分析过了:
11.png
上图的重点是调用 call 0x(nnnn)1390,我们按 F7 进入该函数,如下图所示:
12.png
这里就开始进入注册码加解密阶段了,上图中可以看到一个字符串,这个就是一个密码串,后面需要用到,如下所示:
[Asm] 纯文本查看 复制代码
;密码字符串
786371C2  57 39 4B 38 35 57 56 6E 6B 39 42 6C 43 71 4D 38  W9K85WVnk9BlCqM8
786371D2  66 34 33 72 53 77 5A 36 54 33 37 34 38 62 34 34  f43rSwZ6T3748b44

在调用 call 0x(nnnn)3AD0 后面,是对另一个常量字符串进行一系列变换。这个字符串为:
[Asm] 纯文本查看 复制代码
;另一个密码字符串
786371E6  37 4B 62 33 78 62 6F 49 32 36 38 49              7Kb3xboI268I


下面是本函数的具体代码:
[Asm] 纯文本查看 复制代码
78611390     83EC 0C              sub     esp, 0C
78611393     53                   push    ebx
78611394     55                   push    ebp
78611395     56                   push    esi
78611396     57                   push    edi
78611397     B8 C2716378          mov     eax, 786371C2                            ; ASCII "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
7861139C     E8 2F270000          call    78613AD0                                 ; SN加密, key1
786113A1     0FB615 ED716378      movzx   edx, byte ptr [786371ED]                 ; 7Kb3xboI268I[7]
786113A8     0FB605 EC716378      movzx   eax, byte ptr [786371EC]                 ; 7Kb3xboI268I[6]
786113AF     0FB60D EB716378      movzx   ecx, byte ptr [786371EB]                 ; 7Kb3xboI268I[5]
786113B6     C1E2 08              shl     edx, 8
786113B9     0BD0                 or      edx, eax
786113BB     0FB605 EA716378      movzx   eax, byte ptr [786371EA]                 ; 7Kb3xboI268I[4]
786113C2     C1E2 08              shl     edx, 8
786113C5     0BD1                 or      edx, ecx
786113C7     0FB60D F1716378      movzx   ecx, byte ptr [786371F1]                 ; 7Kb3xboI268I[11]
786113CE     C1E2 08              shl     edx, 8
786113D1     0BD0                 or      edx, eax                                 ; edx = 0x496F6278
786113D3     0FB605 F0716378      movzx   eax, byte ptr [786371F0]                 ; 7Kb3xboI268I[10]
786113DA     C1E1 08              shl     ecx, 8
786113DD     0BC8                 or      ecx, eax
786113DF     0FB605 EF716378      movzx   eax, byte ptr [786371EF]                 ; 7Kb3xboI268I[9]
786113E6     C1E1 08              shl     ecx, 8
786113E9     0BC8                 or      ecx, eax
786113EB     0FB605 EE716378      movzx   eax, byte ptr [786371EE]                 ; 7Kb3xboI268I[8]
786113F2     C1E1 08              shl     ecx, 8
786113F5     0BC8                 or      ecx, eax                                 ; ecx = 0x49383632
786113F7     03D1                 add     edx, ecx                                 ; edx = 0x92A798AA
786113F9     E8 02250000          call    78613900                                 ; 再次加密 SN, key2
786113FE     0FB63D C1716378      movzx   edi, byte ptr [786371C1]                 ; 0x9B[17], ch1
78611405     0FB60D C0716378      movzx   ecx, byte ptr [786371C0]                 ; 0xA4[16], ch2
7861140C     0FB615 BF716378      movzx   edx, byte ptr [786371BF]                 ; 0xB6[15], ch3
78611413     0FB61D BD716378      movzx   ebx, byte ptr [786371BD]                 ; 0xC0[13], ch4
7861141A     0FB605 BE716378      movzx   eax, byte ptr [786371BE]                 ; 0x6A[14], ch5
78611421     0FB62D B9716378      movzx   ebp, byte ptr [786371B9]                 ; 0x9A[9],  ch6
78611428     C1E7 08              shl     edi, 8
7861142B     0BF9                 or      edi, ecx
7861142D     0FB60D BC716378      movzx   ecx, byte ptr [786371BC]                 ; 0x8F[12]
78611434     C1E3 08              shl     ebx, 8
78611437     0BD9                 or      ebx, ecx
78611439     0FB60D B8716378      movzx   ecx, byte ptr [786371B8]                 ; 0xA2[8]
78611440     C1E7 08              shl     edi, 8
78611443     0BFA                 or      edi, edx
78611445     0FB615 BB716378      movzx   edx, byte ptr [786371BB]                 ; 0X9A[11]
7861144C     C1E5 08              shl     ebp, 8
7861144F     C1E3 08              shl     ebx, 8
78611452     0BE9                 or      ebp, ecx
78611454     0FB60D B4716378      movzx   ecx, byte ptr [786371B4]                 ; 0xDF[4]
7861145B     C1E7 08              shl     edi, 8
7861145E     0BF8                 or      edi, eax                                 ; edi = 0x9BA4B66A
78611460     0FB605 BA716378      movzx   eax, byte ptr [786371BA]                 ; 0x86[10]
78611467     0BDA                 or      ebx, edx
78611469     0FB615 B7716378      movzx   edx, byte ptr [786371B7]                 ; 0xB1[7]
78611470     C1E5 08              shl     ebp, 8
78611473     C1E3 08              shl     ebx, 8
78611476     0BD8                 or      ebx, eax                                 ; ebx = 0xC08F9A86
78611478     0FB605 B6716378      movzx   eax, byte ptr [786371B6]                 ; 0x9E[6]
7861147F     0BEA                 or      ebp, edx
78611481     0FB615 B3716378      movzx   edx, byte ptr [786371B3]                 ; 0xD9[3]
78611488     C1E5 08              shl     ebp, 8
7861148B     0BE8                 or      ebp, eax                                 ; ebp = 0x9AA2B19E
7861148D     0FB605 B5716378      movzx   eax, byte ptr [786371B5]                 ; 0xA4[5]
78611494     C1E0 08              shl     eax, 8
78611497     0BC1                 or      eax, ecx
78611499     0FB60D B2716378      movzx   ecx, byte ptr [786371B2]                 ; 0xAD[2]
786114A0     C1E0 08              shl     eax, 8
786114A3     0BC2                 or      eax, edx
786114A5     C1E0 08              shl     eax, 8
786114A8     0BC1                 or      eax, ecx
786114AA     894424 18            mov     dword ptr [esp+18], eax                  ; eax = 0xA4DFD9AD
786114AE     BE 54906378          mov     esi, 78639054                            ; sn_ptr ===> sn_int[]
786114B3     C74424 14 03000000   mov     dword ptr [esp+14], 3                    ; int n = 3
786114BB     EB 07                jmp     short 786114C4
786114BD     8D49 00              lea     ecx, dword ptr [ecx]                     ; 无用
786114C0     8B4424 18            mov     eax, dword ptr [esp+18]                  ; eax = 0xA4DFD9AD
786114C4     57                   push    edi                                      ; 0x9BA4B66A
786114C5     53                   push    ebx                                      ; 0xC08F9A86
786114C6     55                   push    ebp                                      ; 0x9AA2B19E
786114C7     50                   push    eax                                      ; 0xA4DFD9AD
786114C8     E8 D3240000          call    786139A0                                 ; 第三次SN加密处理,循环处理3次。key0
786114CD     8B4424 24            mov     eax, dword ptr [esp+24]                  ; n
786114D1     48                   dec     eax                                      ; n--
786114D2     83C4 10              add     esp, 10
786114D5     83C6 08              add     esi, 8                                   ; sn_ptr += 8, 指向下一个2段SN
786114D8     894424 14            mov     dword ptr [esp+14], eax                  ; n
786114DC     85C0                 test    eax, eax
786114DE   ^ 75 E0                jnz     short 786114C0
786114E0     8A0D 6A906378        mov     cl, byte ptr [7863906A]                  ; sn_int[22]
786114E6     8A15 6B906378        mov     dl, byte ptr [7863906B]                  ; sn_int[23]
786114EC     B8 54906378          mov     eax, 78639054                            ; eax === sn_int[]
786114F1     8A18                 mov     bl, byte ptr [eax]                       ; ch = sn_int[0]
786114F3     2ADA                 sub     bl, dl
786114F5     32D9                 xor     bl, cl
786114F7     8818                 mov     byte ptr [eax], bl                       ; sn[0] = (sn[0] - CRC[1])^CRC[0]
786114F9     40                   inc     eax
786114FA     3D 6A906378          cmp     eax, 7863906A
786114FF   ^ 72 F0                jb      short 786114F1                           ; 循环22次,sn[0]~sn[21]
78611501     5F                   pop     edi
78611502     5E                   pop     esi
78611503     5D                   pop     ebp
78611504     5B                   pop     ebx
78611505     83C4 0C              add     esp, 0C
78611508     C3                   retn

可以看到,这是一个加密函数总入口。
我们先看看 call 0x(nnnn)3AD0 加密函数,如下图所示:
13.png

如上图所示,该函数又两次调用另一个函数 call 0x(nnnn)3A90,进入该函数看看,如下图所示:
14.png

这个函数对密码进行了一些转换操作。


返回上一级函数,看看函数后面,如下图所示:
15.png

就是循环6次,对注册码前6段进行加密运算,循环体靠后面的代码也是对密码进行转换运算,看起来比较复杂,但有一大段代码是用将4字节转换成整数,再将加密后的整数转换成4字节数据保存。
具体代码如下:
[Asm] 纯文本查看 复制代码
; SN 加密
78613AD0     53                   push    ebx
78613AD1     55                   push    ebp
78613AD2     8BD8                 mov     ebx, eax                                 ; ebx ===> keyStr
78613AD4     0FB62B               movzx   ebp, byte ptr [ebx]                      ; W9K85WVnk9BlCqM8f43rSwZ6T3748b44
78613AD7     56                   push    esi
78613AD8     8BCD                 mov     ecx, ebp                                 ; ch = (* keyStr)
78613ADA     57                   push    edi
78613ADB     81F1 33323641        xor     ecx, 41363233
78613AE1     BF 06000000          mov     edi, 6                                   ; n=6, 注册码前6个部分
78613AE6     E8 A5FFFFFF          call    78613A90                                 ; chk1 = func(ch, keyStr);
78613AEB     8BF0                 mov     esi, eax                                 ; esi == chk1
78613AED     8BCD                 mov     ecx, ebp                                 ; ch = (* keyStr)
78613AEF     C1E1 08              shl     ecx, 8                                   ; ch <<= 8
78613AF2     33CE                 xor     ecx, esi                                 ; ch ^= chk
78613AF4     8BC3                 mov     eax, ebx                                 ; eax ===> keyStr
78613AF6     E8 95FFFFFF          call    78613A90                                 ; chk2 = func(ch, keyStr);
78613AFB     BA 56906378          mov     edx, 78639056                            ; ptr = &sn_int[2];
78613B00     0FB64A 01            movzx   ecx, byte ptr [edx+1]                    ; ch1 = * (ptr + 1)
78613B04     0FB61A               movzx   ebx, byte ptr [edx]                      ; ch2 = * ptr
78613B07     C1E1 08              shl     ecx, 8
78613B0A     0BCB                 or      ecx, ebx                                 ; ch1 = (ch1<<8) + ch2;
78613B0C     0FB65A FF            movzx   ebx, byte ptr [edx-1]                    ; ch2 = (* (ptr-1));
78613B10     C1E1 08              shl     ecx, 8
78613B13     0BCB                 or      ecx, ebx                                 ; ch1 = (ch1<<8) + ch2;
78613B15     0FB65A FE            movzx   ebx, byte ptr [edx-2]                    ; ch2 = (* (ptr-2));
78613B19     C1E1 08              shl     ecx, 8
78613B1C     0BCB                 or      ecx, ebx                                 ; ch1 = (ch1<<8) + ch2;
78613B1E     33C8                 xor     ecx, eax                                 ; chk = ch1 ^ chk2;
78613B20     8BD9                 mov     ebx, ecx                                 ; chk[1] = 0x6208A13F
78613B22     C1EB 08              shr     ebx, 8                                   ; chk >>= 8;
78613B25     884A FE              mov     byte ptr [edx-2], cl
78613B28     885A FF              mov     byte ptr [edx-1], bl
78613B2B     8BD9                 mov     ebx, ecx
78613B2D     C1E9 18              shr     ecx, 18
78613B30     884A 01              mov     byte ptr [edx+1], cl
78613B33     C1EB 10              shr     ebx, 10
78613B36     0FB6C8               movzx   ecx, al                                  ; ch = ((unsigned char)chk2)
78613B39     83E1 1F              and     ecx, 1F                                  ; ch = chk2 & 0x1F;
78613B3C     881A                 mov     byte ptr [edx], bl
78613B3E     83C2 04              add     edx, 4                                   ; ptr += 4, 指向SN下一部分
78613B41     D3C6                 rol     esi, cl                                  ; chk1 = (chk1<<ch) + (chk1>>(32-ch));
78613B43     8BCE                 mov     ecx, esi                                 ; ch1 = chk1;
78613B45     C1E9 08              shr     ecx, 8                                   ; ch1 >>= 8;
78613B48     8BDE                 mov     ebx, esi                                 ; ch2 = chk1;
78613B4A     33D8                 xor     ebx, eax                                 ; ch2 ^= chk2;
78613B4C     83E1 1F              and     ecx, 1F                                  ; ch1 &= 0x1F;
78613B4F     D3CB                 ror     ebx, cl                                  ; ch2 = (ch2 >> ch1) + (ch2 << (32-ch1));
78613B51     8BC3                 mov     eax, ebx                                 ; chk2 = ch2;
78613B53     03F0                 add     esi, eax                                 ; chk1 += chk2;
78613B55     83EF 01              sub     edi, 1                                   ; i--
78613B58   ^ 75 A6                jnz     short 78613B00
78613B5A     5F                   pop     edi
78613B5B     5E                   pop     esi
78613B5C     5D                   pop     ebp
78613B5D     5B                   pop     ebx
78613B5E     C3                   retn

内部函数如下:
[Asm] 纯文本查看 复制代码
; 子过程:
78613A90     53                   push    ebx
78613A91     56                   push    esi
78613A92     8BF0                 mov     esi, eax                                 ; int ch2=ecx;
78613A94     33DB                 xor     ebx, ebx                                 ; i=0
78613A96     8A06                 mov     al, byte ptr [esi]                       ; al = (* keyStr)
78613A98     0FB6D0               movzx   edx, al                                  ; ch1 = (unsigned)(* keyStr);
78613A9B     C1E2 08              shl     edx, 8                                   ; ch1 <<= 8;
78613A9E     33CA                 xor     ecx, edx                                 ; ch2 ^= ch1;
78613AA0     0FB6D3               movzx   edx, bl
78613AA3     33D1                 xor     edx, ecx                                 ; ch1 = (unsigned)i ^ ch2;
78613AA5     81C2 6B613470        add     edx, 7034616B                            ; ch1 += 0x7034616B;
78613AAB     0FB6CA               movzx   ecx, dl                                  ; ch2 = (unsigned char)ch1;
78613AAE     83E1 1F              and     ecx, 1F                                  ; ch2 &= 0x1F;
78613AB1     D3CA                 ror     edx, cl                                  ; ch1 = (ch1 >> ch2) + (ch1<<(32-ch2));
78613AB3     81C3 FFFF0000        add     ebx, 0FFFF                               ; i--; //i += 0xFFFF
78613AB9     81F2 A7A57283        xor     edx, 8372A5A7                            ; ch1 ^= 0x8372A5A7
78613ABF     8BCA                 mov     ecx, edx                                 ; ch2 = ch1;
78613AC1     66:85DB              test    bx, bx
78613AC4   ^ 75 DA                jnz     short 78613AA0
78613AC6     46                   inc     esi                                      ; keyStr ++
78613AC7     84C0                 test    al, al
78613AC9   ^ 75 CB                jnz     short 78613A96
78613ACB     5E                   pop     esi
78613ACC     8BC1                 mov     eax, ecx
78613ACE     5B                   pop     ebx
78613ACF     C3                   retn

上面是用索引对密码串进行密码转换运算。
从上面的函数返回后,如下图所示,执行前面函数后面的密码转换代码:
16.png
这一段转换代码就是将常量字符串“7Kb3xboI268I”后8个字符,组成2个4字节的整数,然后相加,存于 edx,作为后面 call 0x(nnnn)3900的参数。
下面进入函数 call 0x(nnnn)3900,如下图所示:
17.png
这个函数里又有大段的代码将4字节组合成一个整数,然后再将整数分拆成4字节保存。前面一截代码是对密码进行一些变换。
接下来函数进行加密运算,最后面又是分拆保存,如下图所示:
18.png

加密指令代码就是上图中间几条指令(0xnnnn3966~0xnnnn396C),主要是相减和异或运算等。
下面是上面加密操作的函数代码:
[Asm] 纯文本查看 复制代码
; 加密函数
78613900     53                   push    ebx                                      ; 再次对序列号进行处理
78613901     55                   push    ebp
78613902     56                   push    esi
78613903     57                   push    edi
78613904     BD 06000000          mov     ebp, 6                                   ; int n = 6
78613909     33F6                 xor     esi, esi                                 ; unsigned long sn_origin=0
7861390B     B9 56906378          mov     ecx, 78639056                            ; ecx==>SN_int[2]
78613910     81EA DBEFBD76        sub     edx, 76BDEFDB                            ; key -= 0x76BDEFDB, 1BE9A8CF = 92A798AA-76BDEFDB
78613916     66:0FB6FA            movzx   di, dl                                   ; key1 = (unsigned char)key;
7861391A     8BC2                 mov     eax, edx
7861391C     C1E8 10              shr     eax, 10                                  ; key2 = key >> 0x10;
7861391F     66:C1E7 08           shl     di, 8                                    ; key1 <<= 8;
78613923     C1EA 08              shr     edx, 8                                   ; key >>= 8;
78613926     0FB7FF               movzx   edi, di                                  ; key3 = (unsigned long)key1;
78613929     0FB6D2               movzx   edx, dl                                  ; key = (unsigned char)key;
7861392C     0BFA                 or      edi, edx                                 ; key3 += key;
7861392E     8BD0                 mov     edx, eax                                 ; key = key2;
78613930     66:0FB6C0            movzx   ax, al                                   ; key0 = (unsigned char)key2;
78613934     66:C1E0 08           shl     ax, 8                                    ; key0 <<= 8;
78613938     C1EA 08              shr     edx, 8                                   ; key >>= 8;
7861393B     C1E7 10              shl     edi, 10                                  ; key3 <<= 0x10;
7861393E     0FB6D2               movzx   edx, dl                                  ; key = (unsigned char)key;
78613941     0BFA                 or      edi, edx                                 ; key3 += key;
78613943     0FB7D0               movzx   edx, ax                                  ; key = (unsigned long)key0;
78613946     0FB641 01            movzx   eax, byte ptr [ecx+1]                    ; *(sn_int+1)
7861394A     0BD7                 or      edx, edi                                 ; key += key3; // 0xCFA8E91B
7861394C     0FB639               movzx   edi, byte ptr [ecx]                      ; *(sn_int)
7861394F     C1E0 08              shl     eax, 8
78613952     0BC7                 or      eax, edi
78613954     0FB679 FF            movzx   edi, byte ptr [ecx-1]                    ; *(sn_int-1)
78613958     C1E0 08              shl     eax, 8
7861395B     0BC7                 or      eax, edi
7861395D     0FB679 FE            movzx   edi, byte ptr [ecx-2]                    ; *(sn_int-2)
78613961     C1E0 08              shl     eax, 8
78613964     0BC7                 or      eax, edi                                 ; eax == sn_part
78613966     8BF8                 mov     edi, eax                                 ; sn_tmp = sn_part;
78613968     2BC2                 sub     eax, edx                                 ; sn_part -= key;
7861396A     33C6                 xor     eax, esi                                 ; sn_part ^= sn_origin;
7861396C     8BD8                 mov     ebx, eax                                 ; sn_new = sn_part; ///925FB824
7861396E     C1EB 08              shr     ebx, 8
78613971     8859 FF              mov     byte ptr [ecx-1], bl                     ; save
78613974     8BD8                 mov     ebx, eax
78613976     8841 FE              mov     byte ptr [ecx-2], al                     ; save
78613979     C1EB 10              shr     ebx, 10
7861397C     C1E8 18              shr     eax, 18
7861397F     8819                 mov     byte ptr [ecx], bl                       ; save
78613981     8841 01              mov     byte ptr [ecx+1], al                     ; save
78613984     83C1 04              add     ecx, 4                                   ; sn_ptr += 4; /// 下一部分
78613987     83ED 01              sub     ebp, 1                                   ; n --
7861398A     8BF7                 mov     esi, edi                                 ; sn_origin = sn_tmp
7861398C   ^ 75 82                jnz     short 78613910
7861398E     5F                   pop     edi
7861398F     5E                   pop     esi
78613990     5D                   pop     ebp
78613991     5B                   pop     ebx
78613992     C3                   retn

从上面函数返回后,又对另一组常量数组进行变换,形成4个整数,数组如下:
[Asm] 纯文本查看 复制代码
; 数组数据
786371B0  BD 88 AD D9 DF A4 9E B1 A2 9A 86 9A 8F C0 6A B6 A4 9B   


代码如下所示:
[Asm] 纯文本查看 复制代码
786113FE     0FB63D C1716378      movzx   edi, byte ptr [786371C1]                 ; 0x9B[17], ch1
78611405     0FB60D C0716378      movzx   ecx, byte ptr [786371C0]                 ; 0xA4[16], ch2
7861140C     0FB615 BF716378      movzx   edx, byte ptr [786371BF]                 ; 0xB6[15], ch3
78611413     0FB61D BD716378      movzx   ebx, byte ptr [786371BD]                 ; 0xC0[13], ch4
7861141A     0FB605 BE716378      movzx   eax, byte ptr [786371BE]                 ; 0x6A[14], ch5
78611421     0FB62D B9716378      movzx   ebp, byte ptr [786371B9]                 ; 0x9A[9],  ch6
78611428     C1E7 08              shl     edi, 8
7861142B     0BF9                 or      edi, ecx
7861142D     0FB60D BC716378      movzx   ecx, byte ptr [786371BC]                 ; 0x8F[12]
78611434     C1E3 08              shl     ebx, 8
78611437     0BD9                 or      ebx, ecx
78611439     0FB60D B8716378      movzx   ecx, byte ptr [786371B8]                 ; 0xA2[8]
78611440     C1E7 08              shl     edi, 8
78611443     0BFA                 or      edi, edx
78611445     0FB615 BB716378      movzx   edx, byte ptr [786371BB]                 ; 0X9A[11]
7861144C     C1E5 08              shl     ebp, 8
7861144F     C1E3 08              shl     ebx, 8
78611452     0BE9                 or      ebp, ecx
78611454     0FB60D B4716378      movzx   ecx, byte ptr [786371B4]                 ; 0xDF[4]
7861145B     C1E7 08              shl     edi, 8
7861145E     0BF8                 or      edi, eax                                 ; edi = 0x9BA4B66A
78611460     0FB605 BA716378      movzx   eax, byte ptr [786371BA]                 ; 0x86[10]
78611467     0BDA                 or      ebx, edx
78611469     0FB615 B7716378      movzx   edx, byte ptr [786371B7]                 ; 0xB1[7]
78611470     C1E5 08              shl     ebp, 8
78611473     C1E3 08              shl     ebx, 8
78611476     0BD8                 or      ebx, eax                                 ; ebx = 0xC08F9A86
78611478     0FB605 B6716378      movzx   eax, byte ptr [786371B6]                 ; 0x9E[6]
7861147F     0BEA                 or      ebp, edx
78611481     0FB615 B3716378      movzx   edx, byte ptr [786371B3]                 ; 0xD9[3]
78611488     C1E5 08              shl     ebp, 8
7861148B     0BE8                 or      ebp, eax                                 ; ebp = 0x9AA2B19E
7861148D     0FB605 B5716378      movzx   eax, byte ptr [786371B5]                 ; 0xA4[5]
78611494     C1E0 08              shl     eax, 8
78611497     0BC1                 or      eax, ecx
78611499     0FB60D B2716378      movzx   ecx, byte ptr [786371B2]                 ; 0xAD[2]
786114A0     C1E0 08              shl     eax, 8
786114A3     0BC2                 or      eax, edx
786114A5     C1E0 08              shl     eax, 8
786114A8     0BC1                 or      eax, ecx
786114AA     894424 18            mov     dword ptr [esp+18], eax                  ; eax = 0xA4DFD9AD
786114AE     BE 54906378          mov     esi, 78639054                            ; sn_ptr ===> sn_int[]
786114B3     C74424 14 03000000   mov     dword ptr [esp+14], 3                    ; int n = 3
786114BB     EB 07                jmp     short 786114C4
786114BD     8D49 00              lea     ecx, dword ptr [ecx]                     ; 无用
786114C0     8B4424 18            mov     eax, dword ptr [esp+18]                  ; eax = 0xA4DFD9AD
786114C4     57                   push    edi                                      ; 0x9BA4B66A
786114C5     53                   push    ebx                                      ; 0xC08F9A86
786114C6     55                   push    ebp                                      ; 0x9AA2B19E
786114C7     50                   push    eax                                      ; 0xA4DFD9AD
786114C8     E8 D3240000          call    786139A0                                 ; 第三次SN加密处理,循环处理3次。

可见,这4个整作为函数 call 0x(nnnn)39A0 的参数,这几个参数也是密码,对 SN 进行处理,我们看看这个函数:
20.png
函数前面没什么看的,又是字节转换整数。看后面的加密,如下图所示:
21.png

这里同时取SN的两段(共64bits),同时进行加密。并且每次重复32轮计算,才得到加密的密文。可以看到,这里的2段SN会交叉加密。


上面加密的代码如下:
[Asm] 纯文本查看 复制代码
786139A0     51                   push    ecx
786139A1     0FB64E 03            movzx   ecx, byte ptr [esi+3]
786139A5     0FB646 02            movzx   eax, byte ptr [esi+2]
786139A9     0FB656 01            movzx   edx, byte ptr [esi+1]
786139AD     C1E1 08              shl     ecx, 8
786139B0     0BC8                 or      ecx, eax
786139B2     0FB606               movzx   eax, byte ptr [esi]
786139B5     C1E1 08              shl     ecx, 8
786139B8     0BCA                 or      ecx, edx
786139BA     0FB656 06            movzx   edx, byte ptr [esi+6]
786139BE     C1E1 08              shl     ecx, 8
786139C1     0BC8                 or      ecx, eax                                 ; ecx == sn_part1,32位SN数据
786139C3     0FB646 07            movzx   eax, byte ptr [esi+7]
786139C7     C1E0 08              shl     eax, 8
786139CA     0BC2                 or      eax, edx
786139CC     0FB656 05            movzx   edx, byte ptr [esi+5]
786139D0     C1E0 08              shl     eax, 8
786139D3     0BC2                 or      eax, edx
786139D5     0FB656 04            movzx   edx, byte ptr [esi+4]
786139D9     53                   push    ebx
786139DA     55                   push    ebp
786139DB     C1E0 08              shl     eax, 8
786139DE     57                   push    edi
786139DF     0BC2                 or      eax, edx                                 ; eax == sn_part2,另外32位SN数据
786139E1     BF 20000000          mov     edi, 20                                  ; int n = 0x20;  // 循环次数,32次
786139E6     BA 2037EFC6          mov     edx, C6EF3720                            ; key = 0xC6EF3720
786139EB     4F                   dec     edi                                      ; n--
786139EC     897C24 0C            mov     dword ptr [esp+C], edi                   ; n
786139F0     8BD9                 mov     ebx, ecx                                 ; sn11 = sn_part1;
786139F2     335C24 1C            xor     ebx, dword ptr [esp+1C]                  ; sn11 = sn_part1 ^ key3;
786139F6     8BF9                 mov     edi, ecx                                 ; sn12 = sn_part1;
786139F8     035C24 20            add     ebx, dword ptr [esp+20]                  ; sn11 = sn11 + key4;
786139FC     C1EF 05              shr     edi, 5                                   ; sn12 >>= 5;
786139FF     33FA                 xor     edi, edx                                 ; sn12 ^= key;
78613A01     03DF                 add     ebx, edi                                 ; sn11 += sn12;
78613A03     8BE9                 mov     ebp, ecx                                 ; sn13 = sn_part1;
78613A05     C1E5 04              shl     ebp, 4                                   ; sn13 <<= 4;
78613A08     03EB                 add     ebp, ebx                                 ; sn13 += sn11;
78613A0A     2BC5                 sub     eax, ebp                                 ; sn_part2 -= sn13;
78613A0C     8BD8                 mov     ebx, eax
78613A0E     335C24 14            xor     ebx, dword ptr [esp+14]                  ; sn11 = sn_part2 ^ key1;
78613A12     8BF8                 mov     edi, eax                                 ; sn12 = sn_part2;
78613A14     035C24 18            add     ebx, dword ptr [esp+18]                  ; sn11 = sn11 + key2;
78613A18     C1EF 05              shr     edi, 5                                   ; sn12 >>= 5;
78613A1B     33FA                 xor     edi, edx                                 ; sn12 ^= key;
78613A1D     8BE8                 mov     ebp, eax                                 ; sn13 = sn_part2;
78613A1F     03DF                 add     ebx, edi                                 ; sn11 += sn12;
78613A21     8B7C24 0C            mov     edi, dword ptr [esp+C]                   ; n
78613A25     C1E5 04              shl     ebp, 4                                   ; sn13 <<= 4;
78613A28     03EB                 add     ebp, ebx                                 ; sn13 += sn11;
78613A2A     2BCD                 sub     ecx, ebp                                 ; sn_part1 -= sn13;
78613A2C     81C2 4786C861        add     edx, 61C88647                            ; key += 0x61C88647;
78613A32     85FF                 test    edi, edi
78613A34   ^ 77 B5                ja      short 786139EB                           ; 循环执行32次
78613A36     33FF                 xor     edi, edi                                 ; 0
78613A38     0BC7                 or      eax, edi                                 ; sn_part2 = 0x4C22825B
78613A3A     33D2                 xor     edx, edx                                 ; 0
78613A3C     0BD1                 or      edx, ecx                                 ; sn_part1 = 0x4F16BACE
78613A3E     8BCA                 mov     ecx, edx                                 ; sn_part1 = 0x4F16BACE
78613A40     8BF8                 mov     edi, eax                                 ; sn_part2 = 0x4C22825B
78613A42     0FACF9 08            shrd    ecx, edi, 8                              ; save to sn_int, edi最低8bits移到ecx的最高8bits
78613A46     884E 01              mov     byte ptr [esi+1], cl                     ; save
78613A49     C1EF 08              shr     edi, 8
78613A4C     8BCA                 mov     ecx, edx
78613A4E     8BF8                 mov     edi, eax
78613A50     0FACF9 10            shrd    ecx, edi, 10
78613A54     884E 02              mov     byte ptr [esi+2], cl                     ; save
78613A57     8816                 mov     byte ptr [esi], dl                       ; save
78613A59     8BC8                 mov     ecx, eax
78613A5B     0FACCA 18            shrd    edx, ecx, 18
78613A5F     C1E9 18              shr     ecx, 18
78613A62     8856 03              mov     byte ptr [esi+3], dl                     ; save
78613A65     8BD0                 mov     edx, eax
78613A67     C1EF 10              shr     edi, 10
78613A6A     8BC8                 mov     ecx, eax
78613A6C     5F                   pop     edi
78613A6D     8856 04              mov     byte ptr [esi+4], dl                     ; save
78613A70     C1E9 08              shr     ecx, 8
78613A73     C1EA 10              shr     edx, 10
78613A76     C1E8 18              shr     eax, 18
78613A79     5D                   pop     ebp
78613A7A     884E 05              mov     byte ptr [esi+5], cl                     ; save
78613A7D     8856 06              mov     byte ptr [esi+6], dl                     ; save
78613A80     8846 07              mov     byte ptr [esi+7], al                     ; save
78613A83     5B                   pop     ebx
78613A84     59                   pop     ecx
78613A85     C3                   retn



这里的加密强度最强,主要加密也就在这里,后面的算法逆向难点也在这段代码,其它几次加密都比较好逆向。


退出这个函数后,回到上一级加密函数,如下图所示:
22.png
可以看到,将通过以上多个步骤加密后的SN数据,取出其中的SN[22]、SN[23]两个字节,并用这两个字节对注册码的前22个字节再进行一次加密(减-异或)。


从上面函数返回后,返回到SN主验证函数,如下图所示:
23.png
这里又生成一次用户名的 CRC 值(查表索引生成方式与上一次不同),取其低16位,与前面完成加密后的SN 中的 SN[18]和SN[19]两个字节组成的双字进行比较(0xnnnn2612 处),如果相等,则注册码有效,否则注册码无效。


可以看到,注册码中只有2个字节是用来判断是否注册成功的标志。只要这两个字节正确,就可以注册成功。


我们修改 al == 1后(手动让注册成功),退出函数,来到下图所示位置:
24.png
这里是导出函数入口,函数名为:TRRegGetStatus_BL(),再次 F8 返回主程序,如下所示,显示注册验证调用处:
25.png
这里有多个对库函数的调用,按 F9 运行,进入软件,按”About",显示关于消息框,如下图所示:
26.png
由于前面手动改成 al==1,所以这里显示注册成功了。


好了,加密函数的分析就到这里,下面的代码是我将加密函数的汇编代码翻译成 C 语言代码,可以原版还原出其算法,计算过程和计算结果都一致,也没有简化,与汇编指令有极大的相似度,便于大家对照分析,也是后面进行逆向分析的基础:
[C++] 纯文本查看 复制代码
#include <iostream>
#include "crc_table.h"

typedef unsigned char  UCHAR;
typedef unsigned short UWORD;
typedef unsigned long  ULONG;

/////////////
unsigned char key0[] = {0xBD, 0x88, 0xAD, 0xD9, 0xDF, 0xA4, 0x9E, 0xB1, 0xA2, 
                        0x9A, 0x86, 0x9A, 0x8F, 0xC0, 0x6A, 0xB6, 0xA4, 0x9B};
//                      0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A
char key1[] = "W9K85WVnk9BlCqM8f43rSwZ6T3748b44";
// 0x384B3957, 0x6E565735, 0x6C42396B, 0x384D7143,
// 0x72333466, 0x365A7753, 0x34373354, 0x34346238
char key2[] = "7Kb3xboI268I";  //// 0x33624B37, 0x496F6278, 0x49383632 

//// test sn: 11111111-22222222-33333333-44444444-55555555-66666666-4D89 
char sn_char_test[] = {0x11, 0x11, 0x11, 0x11, 
                       0x22, 0x22, 0x22, 0x22, 
                                       0x33, 0x33, 0x33, 0x33, 
                                       0x44, 0x44, 0x44, 0x44, 
                                       0x55, 0x55, 0x55, 0x55, 
                                       0x66, 0x66, 0x66, 0x66, 
                                       0x77, 0x77}; ////check

void printSN(int theTimes);

long getCRC(char * buff);
unsigned short getSNCheck(char * sn_char);
long getKeyCheck1(char * key, char * sn_int);
long getKeyCheck2(unsigned long key, char * keyStr);

long getKey2Check1(char * key, char * sn_int);
long getKey2Check2(unsigned long key, char * sn_int);

long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int);

long getSNCheck2(char * sn_int);

long getNameCheck(char * name);

int main(int argc, char** argv) {
        
        char name[] = "solly";

        long nameCRC = getCRC(name);
        ///printf("Name check: 0x%08X\n", nameCRC);

        long organCRC = getCRC(NULL); //// 目前没有用到,为空 

        short snCheck = getSNCheck(sn_char_test);

        unsigned short crc = (short)(nameCRC + organCRC) ^ snCheck;
        
        printf("SN CRC: 0x%04X\n", crc);

        printSN(0); /// 显示注册码 
        
        sn_char_test[24] = (unsigned char)(crc);
        sn_char_test[25] = (unsigned char)(crc>>8);
        
        printSN(1); /// 显示注册码 

        long keyCheck = getKeyCheck1(key1, sn_char_test);
        
        printSN(2); /// 显示注册码 

        getKey2Check1(key2, sn_char_test);
        
        printSN(3); /// 显示注册码 
        
        
        ////
        long sn_check2 = getSNCheck2(sn_char_test);

        printSN(5); /// 显示注册码 
        
        long name_check = getNameCheck(name);
        
        printf("Name check: 0x%08X\n", name_check);
        
        UWORD key = (UWORD) name_check;
        UWORD sn_key = * (UWORD *)&sn_char_test[18];  /// sn_int[19]sn_int[18]
        
        printf("key ok: 0x%04X, key_act: 0x%04X\n", key, sn_key); 

        return 0;
}

void printSN(int theTimes) {
        int n = 26; 
        printf("SN %d:", theTimes);
        for(int i=0; i<n; i++) {
                if(i % 4 == 0) {
                        printf("  ");
                }
                printf("%02X", (unsigned char)sn_char_test[i]);
        }
        printf("\n");
}

long getCRC(char * buff) {
        long crc = 0;
        if(buff == NULL) {
                return 0;
        }
        while(*buff != '\0') {
                crc ^= crc_table[(unsigned int)((*buff) ^ (unsigned char)crc)];
                buff++;
        }
        
        return crc;
}

unsigned short getSNCheck(char * sn_char) {
        unsigned char ch_even = 0;
        unsigned char ch_odd  = 0;
        unsigned short sn_check = 0;
        
        if(sn_char == NULL) {
                return 0;
        }

        for(int i=0; i<24; i++) {
                if(i % 2) {
                        ch_odd += sn_char[i];
                } else {
                        ch_even ^= sn_char[i];
                }
        }

        sn_check = (ch_even<<8) + ch_odd;
        sn_check ^= ((unsigned short)key2[1]<<8) + (unsigned short)key2[0];
        
        return sn_check;
}

/// Key1: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
/// return: E88F784B
long getKeyCheck2(unsigned long key, char * keyStr) {
        unsigned char ch = 0;
        unsigned long ch1 = 0;
        unsigned long ch2 = key;

        do {
                ch = (unsigned char)(* keyStr);
                ch1 = ((unsigned long)ch << 8);
                ch2 ^= ch1;
        
                unsigned short i = 0;
                do {
                        ch1 = (unsigned char)i ^ ch2;
                        ch1 += 0x7034616B;
                        ch2 = (unsigned char)ch1;
                        ch2 &= 0x1F;
                        ch1 = (ch1 >> ch2) + (ch1<<(32-ch2)); /// ROR,用VS编译时需要判断 ch1==0 时,不执行这一行。
                        ch2 = ch1 ^ 0x8372A5A7;
                        i--; //i += 0xFFFF;
                } while(i);
        
                keyStr ++;
        } while(ch);
        
        return ch2;
}

/// Key: "W9K85WVnk9BlCqM8f43rSwZ6T3748b44"
long getKeyCheck1(char * key, char * sn_int) {
        unsigned long ch = 0;
        unsigned long ch1 = 0;
        unsigned long ch2 = 0;
        unsigned long chk = 0;
        unsigned long chk1 = 0;
        unsigned long chk2 = 0;
        
        ch = (unsigned long)(* key);
        ch ^= 0x41363233; ///"326A"   //// 41363264
        printf("ch: 0x%08X\n", ch);
        
        ////
        chk1 = getKeyCheck2(ch, key);
        printf("chk1: 0x%08X\n", chk1);
        ch = (unsigned long)(* key);
        ch <<= 8;
        ch ^= chk1;
        chk2 = getKeyCheck2(ch, key);
        printf("chk2: 0x%08X\n\n", chk2);
        
        //// 取 SN 第1部分形成一个整数 
        unsigned char * ptr = (unsigned char *)&sn_int[2];
        
        for(int i=0; i<6; i++) {
                //// 取每部分4字节形成一个整数 
                ch1 = (unsigned long)(* (ptr+1));
                ch2 = (unsigned long)(* (ptr));
                ch1 = (ch1<<8) + ch2;
                ch2 = (unsigned long)(* (ptr-1));
                ch1 = (ch1<<8) + ch2;
                ch2 = (unsigned long)(* (ptr-2));
                ch1 = (ch1<<8) + ch2;
        
                //// xor
                chk = ch1 ^ chk2;
                printf("SN_chk%d: 0x%08X ==> 0x%08X\n", i+1, ch1, chk);
        
                //// 将整数保存回每部分的4字节数据 
                *(ptr-2) = (char)chk;
                chk >>= 8;
                *(ptr-1) = (char)chk;
                chk >>= 8;
                *(ptr)   = (char)chk;
                chk >>= 8;
                *(ptr+1) = (char)chk;
        
                ////
                ptr += 4; /// SN 下一部分 
        
                ch = chk2 & 0x1F;
                //printf("ch: 0x%08X\n", ch);
                chk1 = (chk1<<ch) + (chk1>>(32-ch));  /// ROL,用VS编译时需要判断 ch1==0 时,不执行这一行。
                ch1 = chk1;
                ch1 >>= 8;
                ch2 = chk1;
                ch2 ^= chk2;
                ch1 &= 0x1F;
                ch2 = (ch2 >> ch1) + (ch2 << (32-ch1)); /// ROR,用VS编译时需要判断 ch1==0 时,不执行这一行。
                chk2 = ch2;
                chk1 += chk2;
                //printf("chk1: 0x%08X, chk2: 0x%08X\n", chk1, chk2);
        }
        
        
        return chk1;
}

long getKey2Check1(char * key, char * sn_int) {
        unsigned long ch = 0;
        unsigned long ch1 = 0;
        unsigned long ch2 = 0;
        unsigned long ch3 = 0;
        unsigned long ch4 = 0;
        unsigned long ch5 = 0;
        unsigned long ch6 = 0;
        
        //// 由key2组合成两个整数 
        ch1 = key[7];
        ch2 = key[6];
        ch3 = key[5];
        ch1 <<=8;
        ch1 += ch2;
        ch2 = key[4];
        ch1 <<=8;
        ch1 += ch3;
        ch3 = key[11];
        ch1 <<=8;
        ch1 += ch2;     /// ch1 = 0x496F6278 (7-6-5-4)
        ch2 = key[10];
        ch3 <<=8;
        ch3 += ch2;
        ch2 = key[9];
        ch3 <<=8;
        ch3 += ch2;
        ch2 = key[8];
        ch3 <<=8;
        ch3 += ch2;  /// ch3 = 0x49383632 (11-10-9-8)
        
        //// 两整数相加 
        ch = ch1 + ch3; //// ch = 0x92A798AA
        long check2 = getKey2Check2(ch, sn_int);
        
        //// 由 key0 组合成 4 个整数
        ch1 = (unsigned long)key0[17]; 
        ch2 = (unsigned long)key0[16]; 
        ch3 = (unsigned long)key0[15]; 
        ch4 = (unsigned long)key0[13]; 
        ch5 = (unsigned long)key0[14]; 
        ch6 = (unsigned long)key0[9];
        ch1 <<= 8;
        ch1 += ch2;
        ch2 = (unsigned long)key0[12]; 
        ch4 <<= 8;
        ch4 += ch2;
        ch2 = (unsigned long)key0[8];
        ch1 <<= 8;
        ch1 += ch3;
        ch3 = (unsigned long)key0[11];
        ch6 <<= 8;
        ch4 <<= 8;
        ch6 += ch2;
        ch2 = (unsigned long)key0[4];
        ch1 <<= 8;
        ch1 += ch5;  //// 0x9BA4B66A (17-16-15-14) 
        ch5 = (unsigned long)key0[10];
        ch4 += ch3;
        ch3 = (unsigned long)key0[7];
        ch6 <<= 8;
        ch4 <<= 8;
        ch4 += ch5;  //// 0xC08F9A86 (13-12-11-10)
        ch5 = (unsigned long)key0[6];
        ch6 += ch3;
        ch3 = (unsigned long)key0[3];
        ch6 <<= 8;
        ch6 += ch5;  //// 0x9AA2B19E (9-8-7-6)
        ch5 = (unsigned long)key0[5];
        ch5 <<= 8;
        ch5 += ch2;
        ch2 = (unsigned long)key0[2];
        ch5 <<= 8;
        ch5 += ch3;
        ch5 <<= 8;
        ch5 += ch2;  //// 0xA4DFD9AD (5-4-3-2)
        
        ////
        char * sn_ptr = &sn_int[0];
        for(int i=0; i<3; i++) {
                getKey0Check(ch5, ch6, ch4, ch1, sn_ptr);
                sn_ptr += 8; /// 指向SN的下一个2段 
        }
        ///
         
}

long getKey2Check2(unsigned long key, char * sn_int) {
        unsigned short key0 = 0;
        unsigned short key1 = 0;
        unsigned long  key2 = 0;
        unsigned long  key3 = 0;
        unsigned long  ch = 0;
        unsigned long  sn_part = 0;
        
        int n = 6;
        unsigned long sn_origin = 0;
        unsigned long sn_new = 0;
        unsigned long sn_tmp = 0;
        
        unsigned char * sn_ptr = (unsigned char *)&sn_int[2];
        do {
                key -= 0x76BDEFDB;
                key1 = (unsigned char)key;
                key2 = key >> 0x10;
                key1 <<= 8;
                key >>= 8;
                key3 = (unsigned long)key1;
                key = (unsigned char)key;
                key3 += key;
                key = key2;
                key0 = (unsigned char)key2;
                key0 <<= 8;
                key >>= 8;
                key3 <<= 0x10;
                key = (unsigned char)key;
                key3 += key;
                key = (unsigned long)key0;
                key += key3; //// 0xCFA8E91B
                //printf("     key: 0x%08X\n", key);
        
                //// 将SN每部分4字节组合成一个整数
                ch = (unsigned long)(* (sn_ptr+1));
                sn_part = ch;
                ch =  (unsigned long)(* (sn_ptr));
                sn_part <<= 8;
                sn_part += ch;
                ch =  (unsigned long)(* (sn_ptr-1));
                sn_part <<= 8;
                sn_part += ch;
                ch =  (unsigned long)(* (sn_ptr-2));
                sn_part <<= 8;
                sn_part += ch;  
                sn_tmp = sn_part; //// save temp 
                //printf("sn_Part1: 0x%08X\n", sn_part);
        
                //// 计算 
                sn_part -= key;
                sn_part ^= sn_origin;
                //printf("sn_Part2: 0x%08X\n", sn_part);
        
                //// 保存 
                * (sn_ptr-2) = (char)sn_part;
                sn_part >>= 8;
                * (sn_ptr-1) = (char)sn_part;
                sn_part >>= 8;
                * (sn_ptr)   = (char)sn_part;
                sn_part >>= 8;
                * (sn_ptr+1) = (char)sn_part;
        
                sn_ptr += 4;  ///// SN 下一段
                n--;
                sn_origin = sn_tmp;  /// SN 上一段的值,作为下一段的异常参数
        } while(n>0); 
        
        return 0;
}

//// Keys0 = {0xA4DFD9AD, 0x9AA2B19E, 0xC08F9A86, 0x9BA4B66A}
long getKey0Check(ULONG key1, ULONG key2, ULONG key3, ULONG key4, char * sn_int) {
        ULONG sn11, sn12, sn13;
        //// 取注册码 
        ULONG sn_part1 = (ULONG)(*(ULONG *)&sn_int[0]);
        ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int[4]);
        
        ULONG key = 0xC6EF3720;
        
        for(int i=0; i<32; i++) {
                sn11 = sn_part1 ^ key3;
                sn11 += key4;
                
                sn12 = sn_part1 >> 5;
                sn12 ^= key;
                
                sn11 += sn12;
                
                sn13 = sn_part1 << 4;
                sn13 += sn11;
                
                sn_part2 -= sn13;
                
                /////
                sn11 = sn_part2 ^ key1;
                sn11 += key2;
                
                sn12 = sn_part2 >> 5;
                sn12 ^= key;
                
                sn11 += sn12;
                
                sn13 = sn_part2 << 4;
                sn13 += sn11;
                
                sn_part1 -= sn13;
                
                key += 0x61C88647;
        }
        //// sn_part1= 0x4F16BACE, sn_part2 = 4C22825B, key = 0x00000000
        *(ULONG *)&sn_int[0] = sn_part1;
        *(ULONG *)&sn_int[4] = sn_part2;
        printSN(6); 
        
        return 0;
}

long getSNCheck2(char * sn_int) {
        char low = sn_int[22]; /// 0x4D
        char up  = sn_int[23]; /// 0x5F
        
        printf("up = 0x%02X, low = 0x%02X\n", up, low);
        
        for(int i=0; i<22; i++) {
                sn_int[i] = (sn_int[i] - up) ^ low;
        }
        
        return (up<<8) + low;
}

long getNameCheck(char * name) {
        ULONG check = 0;
        while(*name != '\0') {
                check = check ^ crc_table[(UCHAR)check ^ (UCHAR)(* name)];
                name ++;
        }
        
        return check;
}



第二部分就到这里,后面第三部分是对上面的代码进行简化和优化,这样方便分析其算法,逆向出注册机代码。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
Pations + 1 + 1 用心讨论,共获提升!

查看全部评分

戰龍在野 发表于 2019-12-27 19:38
水之镜 发表于 2019-12-27 19:04
看着这个软件转换能力还蛮强的 期待试用 哈哈
头像被屏蔽
YI易 发表于 2019-12-27 18:10
提示: 该帖被管理员或版主屏蔽
lm180180 发表于 2019-12-27 18:18
感谢分享 辛苦了!
zxinyun 发表于 2019-12-27 18:21
我用这家呢视频分割合并工具比较多
白晓生 发表于 2019-12-27 18:53
真是高深莫测呀,这回有的耍了,感谢楼主技术分享
无奈123 发表于 2019-12-27 19:54
好多步驟  不容易啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表