吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 14088|回复: 165
上一主题 下一主题

[原创] 德州扑克俱乐部视频

    [复制链接]
跳转到指定楼层
楼主
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(加点下划线脱敏,防止过敏反应),这个公司有好多个音视频相关转换、分切和合并等的工具软件,
好象注册算法和注册码格式都差不多,只是加解密的密码不一样,当然也不一定正确,因为我也没有验证其它程序的算法,只是看了一下密码。
补充:已验证该公司的视频转换软件的算法也是一样的,只是密码不一样,见后面跟贴中的已更新了的注册机的代码中的注释

一、注册码的校验码


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


由 Microaoft VC 9.0 编译,没有加壳,这是一个好消息,不用脱壳了。

再“Scan /t"成 VC8了,没什么影响。


怎么找注册函数入口就不説了,我们先看看其导入表信息,用 Stud_PE 查看:

Stud_PE 也显示没有壳。

可以看到,这是一个基于 QT4 框架开发的软件,QT4是什么就不説了,反正就是一个 MFC 类似的开发框架,跨平台UI套件。


重点在下面:


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


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


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


可以看到后面有一个调用 QtCore4.qWinMain 调用,这个调用是初始化QT的环境,放大看看:

再往下走,如下图所示:

进入程序入口,我们 F7 进入此函数,可以看到如下代码,如下图所示:


这就是调用程序入口,要显示应用的UI了,我们不看这些了,先 F9 进入程序看看,如下图所示:

右上角,有一个”Register“ 按钮,这是注册用的。先看看 "About",如下所示:

显示”unregistered“,未注册。

点”Register“,进行注册,弹出如下所示注册界面:

随便输入注册假码数据,点”OK“,会弹出如下消息框:

表示注册码无效。


下面进入正题,进行注册码算法的跟踪,我们先到 trreg_bl.dll 中下断点,先找到其导出函数:

”右键“-->”查找“-->”所有模块中的名称“,显示如下界面:

找到我们需要的 "TRRegKeyInstall_BL",如上图所示,双击进入函数,如下图所示:

在函数内下一个断点,如上图所示位置。再回到软件,重新回到软件的注册界面点”OK”,马上就会来到我们下的断点处,如下图所示:

在 0x(nnnn)113E call 0x(nnnn)1FF0 处,就是注册码校验的调用。(注,nnnn 表示值不固定,dll 重定位后,高2字节可能会有变化,每次重进软件都可能不一样

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


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


再次按 F7 进入该函数,来到如下位置:

这里调用 call 0x(nnnn)15A0,对注册码以“-”号进行切分,分成7段,不是7段就退出了,返回到上一级函数:

这里又对注册码进行长度验证,长度应为 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。


长度检查失败再往上返回,如下图所示:

当 al==0时,表示格式检查失败。
下面我们来看看,对注册码进行切分并检查的函数 call 0x(nnnn)15A0,如下图所示,我们F9返回界面,重新点“OK”再次进入注册函数,来到下面位置:

可以看到,注册码是由“-”分隔成多个部分的。按 F7 进入 call 0x(nnnn)2B90,如下图所示:


在注册码中,循环查找“-”符号,我们手动在输入的注册码“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                ; 循环查找


如果找到”-“号,则跳转处理,如下图所示:

找到“-”号就返回其所在位置索引值,并从函数返回。

这时还是对注册码进行切分,截取”-“号后面的内容再进行检查。

如下图所示,这个 call 0x(nnnn)2B90,就是对字符串进行切分处理的函数。

在剩下的字符串,再手动改一位为”-“,验证是否会再次切分。如下图 OD 的数据区所示。


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


切分完后,返回到上一级函数,如下图所示:

因为我们改了两次”-“,字符分成了 3 段,eax == 0x03 了,但还是不够7段。
我们  F9 运行,重新返回界面,输入符合要求的 7 段式注册码,如下图所示:

再次”OK“,一路来到下图所示位置:


可以看到,这次 eax == 0x00000007 了,后面的 cmp eax, 7 的检查也会成功了,分段数正确后,来到下面一个循环处:

这里对切分出来的每一段进行格式检查,前6段是一样的检查,第7段另外检查,如下图所示,检查1~6段。

来到下图所示位置,又是一个长度检查:

需要8位长度,上面 OD 数据区显示,我们前面输入的不对,只有5个字符长度,再次 F9 继续回到界面,输入新的注册码:

再一次”OK“,来到各段格式检查处:

由上图可见,在 OD 数据区显示的字符串中,显示长度为 0x08 了,字符串格式见前面的 struct string 的説明。
长度检查完后,再进行字符范围检查,保证每段字符串都可以转化成 16 进制数,如下图所示:

上图中 call 0x(nnnn)1700 就是对字符范围进行检查。如果都没问题,则继续检查下一段:

最后,当前6段都检查完成后,就单独对第7段进行检查,如下图所示:

进入第7段检查,如下图所示:

可以看到,这里要求的长度是4个字节,我们输入的是8个字符,所以还得重新来一次,F9 后再次修改注册码:

按”OK“继续,来到前面的位置,如下图所示:


如上图所示,SN 第7段的长度为 4 了,这一次终于完美达成其对注册码分段格式的要求了。继续 F8 往下走,退出循环,来到下面位置:

这里一个调用 call 0x(nnnn)2AB0,是对刚才切分成7段的注册码重新进行连接,生成一个新字符,这个字符串就不含”-“号了,执行调用后如下图所示:


上图中的 OD 数据区显示了这个字符串的结构,因为长度为 0x34,大于 0x10,结构内只保存了字符串的地址:0x02C91F80,最大长度是 0x3F,实际长度是 0x34。
跟随上面的地址,可以看到这个字符串的内容,如下图所示:

继续 F8 向下走,来到如下图所示位置:

这里的调用是释放7个子分段的字符串了,切分及合并就完成了,退出函数,返回上一级。

这里对合并的字符串进行长度检查,长度必须为 0x34,否则就是无效的注册码。
检查长度合法后,跳转到下一步,进行注册码的有效性检查,如下图所示:

首先,调用 call 0x(nnnn)3B60 将注册码转换成 16 进制数据,如下图所示:

上图中 OD 数据区显示的是已经转换好了的数据。
接下来是对用户名的处理了,如下图所示:

通过分析,这是通过查表方式,对用户名进行 CRC 校验码的计算,如上图所示,OD 数据区是 CRC 的数据表。
对名字计算CRC后,还有一段对 NULL 字符串进行 CRC 计算的代码,猜测是该算法对机构名之类的进行 CRC 验证,但该软件没有用到。
接下来,对注册码进行奇偶的一个检查,如下图所示:

当索引号是奇数,进行累加,为偶数则进行异或,结果分别存于 BL 和 DL,是一个分开的8位校验。
接下来,把刚才计算生成2个字节,组成一个双字(word),同时取一个软件内置的常量字符串的头两个字节,对这个结果进行XOR计算,再将结果与用户名的CRC值XOR计算,如下图所示:

上面的计算结果再与前面的 CRC 值异或,结果为存于 DX,如下图所示:

上面生成的结果,与输入的注册码的最后2字节(SN 第7段转换后16进制数)进行比较,如下图所示:

如果相等,则表示成功,不相等则注册码无效。
按 F9 退出,返回界面修改注册码,如下图所示:

再次点”OK“,来到前面退出的位置,如下图所示:


这一次是相等的了。格式验证过程就结束了。
这个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 运行,会弹出如下消息框:

这只是表示注册码的格式检查全部通过了,注册码正确性检查需要在重启后进行。
如果要对这个检查进行破解,进行如下操作,来到下面位置:

修改最上面那一条指令,如下图所示,修改后的指令为红色:

这样就可以破解掉格式检查。
都成功后,我们退出 DLL 中的函数,来到主程序,看看是在那里调用的这个 trreg_bl 函数,如下图所示:

就是在这个位置调用的。
=================================================
太长了,注册分析和注册机,放在跟帖内写。先到这里吧。。。。

免费评分

参与人数 45吾爱币 +51 热心值 +44 收起 理由
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 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Gentlewang + 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 加密的框图,如下所示:

可以看到,这里也会将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 校验值。
分析完毕!!!!


免费评分

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

查看全部评分

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

二、注册算法分析

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

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


我们用 OD 重新载入软件,F9 运行,OD 直接断了下来,原来我们在格式检查时下的断点还有效,因为重启后还进行了格式检查,我们退出格式检查函数,来到下图所示位置:

上图中 call 0x(nnnn)1FF0就检查注册码格式并保存注册码的调用,我们就是断在了这个函数里面,现在我们禁用所有前面在函数内下的断点,如上图所示,在调用外下一个新断点即可。
最下面的 call 0x(nnnn)1770也是格式检查。


按 F8 往下执行,如下图所示,对注册码字符串进行格式转换,前面已经分析过了:

上图的重点是调用 call 0x(nnnn)1390,我们按 F7 进入该函数,如下图所示:

这里就开始进入注册码加解密阶段了,上图中可以看到一个字符串,这个就是一个密码串,后面需要用到,如下所示:
[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