solly 发表于 2019-12-27 17:58

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

本帖最后由 solly 于 2019-12-29 01:59 编辑

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


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

一、注册码的校验码。


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


由 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 内存中的存贮格式,结构如下:
struct string {
    void * ptr;
    union {
      char * str_ptr;
      char 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 数据区所示,跟踪代码运行情况。
具体代码如下:
78612BC3    8BF3                mov   esi, ebx                      ; esi ===> 注册码
78612BC5    3BF7                cmp   esi, edi
78612BC7    73 26               jnb   short 78612BEF
78612BC9    8DA424 00000000   lea   esp, dword ptr           ; 在SN中查找 "-" 字符
78612BD0    8B4424 1C         mov   eax, dword ptr
78612BD4    0FBE0E            movsx   ecx, byte ptr          ; ecx == sn
78612BD7    8B5424 18         mov   edx, dword ptr
78612BDB    50                  push    eax                           ; 1
78612BDC    51                  push    ecx                           ; sn == 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++
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段校验码的生成和校验的具体代码如下:
78612067   8B4424 18            mov   eax, dword ptr                ; SN, 去掉“-”的注册码
7861206B   BF 10000000            mov   edi, 10
78612070   397C24 2C            cmp   dword ptr , edi               ; 检查最大长度不小于16
78612074   73 04                  jnb   short 7861207A
78612076   8D4424 18            lea   eax, dword ptr
7861207A   6A 1A                  push    1A                                    ; 26 个字节,长度
7861207C   8D4C24 34            lea   ecx, dword ptr                ; 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                       ; cl = name
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
78612099   0FB6F0               movzx   esi, al                                 ; (uint)(crc & 0xFF)
7861209C   33CE                   xor   ecx, esi                              ; long idx = name ^ (crc &0xFF)
7861209E   33048D 78346378      xor   eax, dword ptr          ; crc ^= crc_table
786120A5   8A4A 01                mov   cl, byte ptr                   ; name
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                       ; 另:机构名?暂无用
786120B9   84C9                   test    cl, cl
786120BB   74 1A                  je      short 786120D7
786120BD   8D49 00                lea   ecx, dword ptr
786120C0   0FB6C9               movzx   ecx, cl
786120C3   0FB6D8               movzx   ebx, al
786120C6   33CB                   xor   ecx, ebx
786120C8   33048D 78346378      xor   eax, dword ptr
786120CF   8A4A 01                mov   cl, byte ptr
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                ; 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                       ; al 为奇, ch2 += sn_char
786120E9   EB 02                  jmp   short 786120ED
786120EB   3211                   xor   dl, byte ptr                       ; al 为偶,ch1 ^= sn_char
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                 ; key2
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                ; key2
78612112   66:C1E1 08             shl   cx, 8
78612116   66:0BC8                or      cx, ax                                  ; short sn_check2 = (key2<<8) + key2
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 , al
78612126   C1E8 08                shr   eax, 8
78612129   884424 11            mov   byte ptr , al                   ; 转换成 short 类型
7861212D   0FB74C24 10            movzx   ecx, word ptr
78612132   66:3B4C24 48         cmp   cx, word ptr                    ; check 值与 SN 最后4位比较
78612137   74 2B                  je      short 78612164                        ; 相等则表示成功

上面对比成功后,直接 F9 运行,会弹出如下消息框:

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

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

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

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

solly 发表于 2019-12-27 17:59

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

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

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


现在看看,最关键的一段加密,其代码如下所示:
786139A0   51                   push    ecx
786139A1   0FB64E 03            movzx   ecx, byte ptr
786139A5   0FB646 02            movzx   eax, byte ptr
786139A9   0FB656 01            movzx   edx, byte ptr
786139AD   C1E1 08            shl   ecx, 8
786139B0   0BC8               or      ecx, eax
786139B2   0FB606               movzx   eax, byte ptr
786139B5   C1E1 08            shl   ecx, 8
786139B8   0BCA               or      ecx, edx
786139BA   0FB656 06            movzx   edx, byte ptr
786139BE   C1E1 08            shl   ecx, 8
786139C1   0BC8               or      ecx, eax                                 ; ecx == sn_part1
786139C3   0FB646 07            movzx   eax, byte ptr
786139C7   C1E0 08            shl   eax, 8
786139CA   0BC2               or      eax, edx
786139CC   0FB656 05            movzx   edx, byte ptr
786139D0   C1E0 08            shl   eax, 8
786139D3   0BC2               or      eax, edx
786139D5   0FB656 04            movzx   edx, byte ptr
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 , edi                   ; n
786139F0   8BD9               mov   ebx, ecx                                 ; sn11 = sn_part1;
786139F2   335C24 1C            xor   ebx, dword ptr                   ; sn11 = sn_part1 ^ key3;
786139F6   8BF9               mov   edi, ecx                                 ; sn12 = sn_part1;
786139F8   035C24 20            add   ebx, dword ptr                   ; 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                   ; sn11 = sn_part2 ^ key1;
78613A12   8BF8               mov   edi, eax                                 ; sn12 = sn_part2;
78613A14   035C24 18            add   ebx, dword ptr                   ; 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                    ; 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 , 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 , cl                     ; save
78613A57   8816               mov   byte ptr , 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 , 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 , 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 , cl                     ; save
78613A7D   8856 06            mov   byte ptr , dl                     ; save
78613A80   8846 07            mov   byte ptr , al                     ; save
78613A83   5B                   pop   ebx
78613A84   59                   pop   ecx
78613A85   C3                   retn
先转换成 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);
      ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
      
      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 = sn_part1;
      *(ULONG *)&sn_int = sn_part2;
      printSN(6);
      
      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);
      ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
      
      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 = sn_part1;
      *(ULONG *)&sn_int = 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 算法的基本了解和查阅了一些资料,终于逆向出解密算法如下:
//// 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);
      ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
      
      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 = sn_part1;
      *(ULONG *)&sn_int = sn_part2;
      //printSN(3);
      
      return 0;
}
与加密算法很相似,只是顺序反过来,包括那个计算的密码也是顺序反过来。
完整的注册机代码如下,包括了注册码的加密过程(从输入的假码到完成加密)和逆向解密过程(在加密后的假码中的将正确的用户名CRC值替换原值,并解密返回到可用的注册码SN 0),便于了解SN在加密、解密过程的变化:
#include <iostream>
#include "crc_table.h"

typedef unsigned charUCHAR;
typedef unsigned short UWORD;
typedef unsigned longULONG;
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;/// sn_intsn_int
   
    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 = (unsigned char)(sn_check);
    sn_char_test = (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<<8) + (UWORD)KEY3;
   
    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;
    keyTransform1(key, chk2);
   
    //// 以整数方式存取
    unsigned long * sn = (unsigned long *)&sn_int;
   
    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; // (7-6-5-4)
    ch3 = *(ULONG *)&KEY3; // (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; //(17-16-15-14)
    ch3 = *(ULONG *)&KEY1; //(13-12-11-10)
    ch2 = *(ULONG *)&KEY1; //(9-8-7-6)
    ch1 = *(ULONG *)&KEY1; //(5-4-3-2)
    //// 每次同时处理两段SN
    unsigned long long * sn_llu = (unsigned long long *)(& sn_int);
    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 longkey2 = 0;
    unsigned longkey3 = 0;
    unsigned longch = 0;
    unsigned longsn_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;
    ULONG * sn = (ULONG *)(& sn_int);
   
    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);
    ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
   
    ULONG key = 0xC6EF3720;
/* 32 个 key: key[ i ] = key + 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 = sn_part1;
    *(ULONG *)&sn_int = sn_part2;
    //printSN(3);
   
    return 0;
}

long getSNCheck2(char * sn_int) {
    char low = sn_int; /// 0x4D
    char up= sn_int; /// 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:1111111122222222333333334444444455555555666666667777
SN 1:1111111122222222333333334444444455555555666666664D89
SN 2:3FA1086249A1F060AC07054AAD4ED79DC4E4277D95728E384D89
SN 3:CEBA164F5B82224CAB6AFAAC0A1589E4E1190CCD2407D7E74D89
SN 3:CEBA164F5B82224C4B43745D754702AFE1190CCD2407D7E74D89
SN 3:CEBA164F5B82224C4B43745D754702AFF3D8A75BFE0A4D5F4D89
SN 3:CEBA164F5B82224C4B43745D754702AFF3D8A75BFE0A4D5F4D89
SN 4:2216FABDB16E8EA0A1A958B35BA5EE1DD93405B1D2E64D5F4D89
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 = (UWORD)name_check;/// sn_intsn_int
   
    return name_check;
}

int step1(char * sn_int) {
    char low = sn_int; /// 0x4D
    char hi= sn_int; /// 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);
   
    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);
    ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
   
    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 = sn_part1;
    *(ULONG *)&sn_int = 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;
    ULONG key3 = * (ULONG *)&KEY1;
    ULONG key2 = * (ULONG *)&KEY1;
    ULONG key1 = * (ULONG *)&KEY1;
    //// 64 bits data from sn
    UINT64 * sn_llu = (UINT64 *)(& sn_int);
    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; // (7-6-5-4)
    ULONG key6 = * (ULONG *)&KEY3; // (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;
    ULONG ch2 = (ULONG)keyStr;
   
    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 = chk2;
      //printf("Key trans %d: 0x%08X\n", i, key_trans);

      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;// = {0x7319B02E, 0x42D2836B, 0x7936349F, 0xD9930AE9, 0x2872B191, 0x5EE814F3};
    keyTransform1(key, key_trans);
    //// 以整数方式存取
    ULONG * sn = (ULONG *)&sn_int;
   
    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 = sn_check;/// sn_intsn_int
   
    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 如下:
//// 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

计算结果如下:
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
请按任意键继续. . .

再来一个:
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 校验值。
分析完毕!!!!

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 进入该函数,如下图所示:

这里就开始进入注册码加解密阶段了,上图中可以看到一个字符串,这个就是一个密码串,后面需要用到,如下所示:
;密码字符串
786371C257 39 4B 38 35 57 56 6E 6B 39 42 6C 43 71 4D 38W9K85WVnk9BlCqM8
786371D266 34 33 72 53 77 5A 36 54 33 37 34 38 62 34 34f43rSwZ6T3748b44
在调用 call 0x(nnnn)3AD0 后面,是对另一个常量字符串进行一系列变换。这个字符串为:
;另一个密码字符串
786371E637 4B 62 33 78 62 6F 49 32 36 38 49            7Kb3xboI268I

下面是本函数的具体代码:
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                ; 7Kb3xboI268I
786113A8   0FB605 EC716378      movzx   eax, byte ptr                ; 7Kb3xboI268I
786113AF   0FB60D EB716378      movzx   ecx, byte ptr                ; 7Kb3xboI268I
786113B6   C1E2 08            shl   edx, 8
786113B9   0BD0               or      edx, eax
786113BB   0FB605 EA716378      movzx   eax, byte ptr                ; 7Kb3xboI268I
786113C2   C1E2 08            shl   edx, 8
786113C5   0BD1               or      edx, ecx
786113C7   0FB60D F1716378      movzx   ecx, byte ptr                ; 7Kb3xboI268I
786113CE   C1E2 08            shl   edx, 8
786113D1   0BD0               or      edx, eax                                 ; edx = 0x496F6278
786113D3   0FB605 F0716378      movzx   eax, byte ptr                ; 7Kb3xboI268I
786113DA   C1E1 08            shl   ecx, 8
786113DD   0BC8               or      ecx, eax
786113DF   0FB605 EF716378      movzx   eax, byte ptr                ; 7Kb3xboI268I
786113E6   C1E1 08            shl   ecx, 8
786113E9   0BC8               or      ecx, eax
786113EB   0FB605 EE716378      movzx   eax, byte ptr                ; 7Kb3xboI268I
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                ; 0x9B, ch1
78611405   0FB60D C0716378      movzx   ecx, byte ptr                ; 0xA4, ch2
7861140C   0FB615 BF716378      movzx   edx, byte ptr                ; 0xB6, ch3
78611413   0FB61D BD716378      movzx   ebx, byte ptr                ; 0xC0, ch4
7861141A   0FB605 BE716378      movzx   eax, byte ptr                ; 0x6A, ch5
78611421   0FB62D B9716378      movzx   ebp, byte ptr                ; 0x9A,ch6
78611428   C1E7 08            shl   edi, 8
7861142B   0BF9               or      edi, ecx
7861142D   0FB60D BC716378      movzx   ecx, byte ptr                ; 0x8F
78611434   C1E3 08            shl   ebx, 8
78611437   0BD9               or      ebx, ecx
78611439   0FB60D B8716378      movzx   ecx, byte ptr                ; 0xA2
78611440   C1E7 08            shl   edi, 8
78611443   0BFA               or      edi, edx
78611445   0FB615 BB716378      movzx   edx, byte ptr                ; 0X9A
7861144C   C1E5 08            shl   ebp, 8
7861144F   C1E3 08            shl   ebx, 8
78611452   0BE9               or      ebp, ecx
78611454   0FB60D B4716378      movzx   ecx, byte ptr                ; 0xDF
7861145B   C1E7 08            shl   edi, 8
7861145E   0BF8               or      edi, eax                                 ; edi = 0x9BA4B66A
78611460   0FB605 BA716378      movzx   eax, byte ptr                ; 0x86
78611467   0BDA               or      ebx, edx
78611469   0FB615 B7716378      movzx   edx, byte ptr                ; 0xB1
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                ; 0x9E
7861147F   0BEA               or      ebp, edx
78611481   0FB615 B3716378      movzx   edx, byte ptr                ; 0xD9
78611488   C1E5 08            shl   ebp, 8
7861148B   0BE8               or      ebp, eax                                 ; ebp = 0x9AA2B19E
7861148D   0FB605 B5716378      movzx   eax, byte ptr                ; 0xA4
78611494   C1E0 08            shl   eax, 8
78611497   0BC1               or      eax, ecx
78611499   0FB60D B2716378      movzx   ecx, byte ptr                ; 0xAD
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 , eax                  ; eax = 0xA4DFD9AD
786114AE   BE 54906378          mov   esi, 78639054                            ; sn_ptr ===> sn_int[]
786114B3   C74424 14 03000000   mov   dword ptr , 3                  ; int n = 3
786114BB   EB 07                jmp   short 786114C4
786114BD   8D49 00            lea   ecx, dword ptr                      ; 无用
786114C0   8B4424 18            mov   eax, dword ptr                   ; 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                   ; 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 , eax                  ; n
786114DC   85C0               test    eax, eax
786114DE   ^ 75 E0                jnz   short 786114C0
786114E0   8A0D 6A906378      mov   cl, byte ptr                   ; sn_int
786114E6   8A15 6B906378      mov   dl, byte ptr                   ; sn_int
786114EC   B8 54906378          mov   eax, 78639054                            ; eax === sn_int[]
786114F1   8A18               mov   bl, byte ptr                      ; ch = sn_int
786114F3   2ADA               sub   bl, dl
786114F5   32D9               xor   bl, cl
786114F7   8818               mov   byte ptr , bl                     ; sn = (sn - CRC)^CRC
786114F9   40                   inc   eax
786114FA   3D 6A906378          cmp   eax, 7863906A
786114FF   ^ 72 F0                jb      short 786114F1                           ; 循环22次,sn~sn
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 加密函数,如下图所示:


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


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


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


就是循环6次,对注册码前6段进行加密运算,循环体靠后面的代码也是对密码进行转换运算,看起来比较复杂,但有一大段代码是用将4字节转换成整数,再将加密后的整数转换成4字节数据保存。
具体代码如下:
; SN 加密
78613AD0   53                   push    ebx
78613AD1   55                   push    ebp
78613AD2   8BD8               mov   ebx, eax                                 ; ebx ===> keyStr
78613AD4   0FB62B               movzx   ebp, byte ptr                       ; 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;
78613B00   0FB64A 01            movzx   ecx, byte ptr                   ; ch1 = * (ptr + 1)
78613B04   0FB61A               movzx   ebx, byte ptr                       ; ch2 = * ptr
78613B07   C1E1 08            shl   ecx, 8
78613B0A   0BCB               or      ecx, ebx                                 ; ch1 = (ch1<<8) + ch2;
78613B0C   0FB65A FF            movzx   ebx, byte ptr                   ; 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                   ; 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 = 0x6208A13F
78613B22   C1EB 08            shr   ebx, 8                                 ; chk >>= 8;
78613B25   884A FE            mov   byte ptr , cl
78613B28   885A FF            mov   byte ptr , bl
78613B2B   8BD9               mov   ebx, ecx
78613B2D   C1E9 18            shr   ecx, 18
78613B30   884A 01            mov   byte ptr , 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 , 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
内部函数如下:
; 子过程:
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                      ; 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

上面是用索引对密码串进行密码转换运算。
从上面的函数返回后,如下图所示,执行前面函数后面的密码转换代码:

这一段转换代码就是将常量字符串“7Kb3xboI268I”后8个字符,组成2个4字节的整数,然后相加,存于 edx,作为后面 call 0x(nnnn)3900的参数。
下面进入函数 call 0x(nnnn)3900,如下图所示:

这个函数里又有大段的代码将4字节组合成一个整数,然后再将整数分拆成4字节保存。前面一截代码是对密码进行一些变换。
接下来函数进行加密运算,最后面又是分拆保存,如下图所示:


加密指令代码就是上图中间几条指令(0xnnnn3966~0xnnnn396C),主要是相减和异或运算等。
下面是上面加密操作的函数代码:
; 加密函数
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
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                   ; *(sn_int+1)
7861394A   0BD7               or      edx, edi                                 ; key += key3; // 0xCFA8E91B
7861394C   0FB639               movzx   edi, byte ptr                       ; *(sn_int)
7861394F   C1E0 08            shl   eax, 8
78613952   0BC7               or      eax, edi
78613954   0FB679 FF            movzx   edi, byte ptr                   ; *(sn_int-1)
78613958   C1E0 08            shl   eax, 8
7861395B   0BC7               or      eax, edi
7861395D   0FB679 FE            movzx   edi, byte ptr                   ; *(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 , bl                     ; save
78613974   8BD8               mov   ebx, eax
78613976   8841 FE            mov   byte ptr , al                     ; save
78613979   C1EB 10            shr   ebx, 10
7861397C   C1E8 18            shr   eax, 18
7861397F   8819               mov   byte ptr , bl                     ; save
78613981   8841 01            mov   byte ptr , 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个整数,数组如下:
; 数组数据
786371B0BD 88 AD D9 DF A4 9E B1 A2 9A 86 9A 8F C0 6A B6 A4 9B   

代码如下所示:
786113FE   0FB63D C1716378      movzx   edi, byte ptr                ; 0x9B, ch1
78611405   0FB60D C0716378      movzx   ecx, byte ptr                ; 0xA4, ch2
7861140C   0FB615 BF716378      movzx   edx, byte ptr                ; 0xB6, ch3
78611413   0FB61D BD716378      movzx   ebx, byte ptr                ; 0xC0, ch4
7861141A   0FB605 BE716378      movzx   eax, byte ptr                ; 0x6A, ch5
78611421   0FB62D B9716378      movzx   ebp, byte ptr                ; 0x9A,ch6
78611428   C1E7 08            shl   edi, 8
7861142B   0BF9               or      edi, ecx
7861142D   0FB60D BC716378      movzx   ecx, byte ptr                ; 0x8F
78611434   C1E3 08            shl   ebx, 8
78611437   0BD9               or      ebx, ecx
78611439   0FB60D B8716378      movzx   ecx, byte ptr                ; 0xA2
78611440   C1E7 08            shl   edi, 8
78611443   0BFA               or      edi, edx
78611445   0FB615 BB716378      movzx   edx, byte ptr                ; 0X9A
7861144C   C1E5 08            shl   ebp, 8
7861144F   C1E3 08            shl   ebx, 8
78611452   0BE9               or      ebp, ecx
78611454   0FB60D B4716378      movzx   ecx, byte ptr                ; 0xDF
7861145B   C1E7 08            shl   edi, 8
7861145E   0BF8               or      edi, eax                                 ; edi = 0x9BA4B66A
78611460   0FB605 BA716378      movzx   eax, byte ptr                ; 0x86
78611467   0BDA               or      ebx, edx
78611469   0FB615 B7716378      movzx   edx, byte ptr                ; 0xB1
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                ; 0x9E
7861147F   0BEA               or      ebp, edx
78611481   0FB615 B3716378      movzx   edx, byte ptr                ; 0xD9
78611488   C1E5 08            shl   ebp, 8
7861148B   0BE8               or      ebp, eax                                 ; ebp = 0x9AA2B19E
7861148D   0FB605 B5716378      movzx   eax, byte ptr                ; 0xA4
78611494   C1E0 08            shl   eax, 8
78611497   0BC1               or      eax, ecx
78611499   0FB60D B2716378      movzx   ecx, byte ptr                ; 0xAD
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 , eax                  ; eax = 0xA4DFD9AD
786114AE   BE 54906378          mov   esi, 78639054                            ; sn_ptr ===> sn_int[]
786114B3   C74424 14 03000000   mov   dword ptr , 3                  ; int n = 3
786114BB   EB 07                jmp   short 786114C4
786114BD   8D49 00            lea   ecx, dword ptr                      ; 无用
786114C0   8B4424 18            mov   eax, dword ptr                   ; 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 进行处理,我们看看这个函数:

函数前面没什么看的,又是字节转换整数。看后面的加密,如下图所示:


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


上面加密的代码如下:
786139A0   51                   push    ecx
786139A1   0FB64E 03            movzx   ecx, byte ptr
786139A5   0FB646 02            movzx   eax, byte ptr
786139A9   0FB656 01            movzx   edx, byte ptr
786139AD   C1E1 08            shl   ecx, 8
786139B0   0BC8               or      ecx, eax
786139B2   0FB606               movzx   eax, byte ptr
786139B5   C1E1 08            shl   ecx, 8
786139B8   0BCA               or      ecx, edx
786139BA   0FB656 06            movzx   edx, byte ptr
786139BE   C1E1 08            shl   ecx, 8
786139C1   0BC8               or      ecx, eax                                 ; ecx == sn_part1,32位SN数据
786139C3   0FB646 07            movzx   eax, byte ptr
786139C7   C1E0 08            shl   eax, 8
786139CA   0BC2               or      eax, edx
786139CC   0FB656 05            movzx   edx, byte ptr
786139D0   C1E0 08            shl   eax, 8
786139D3   0BC2               or      eax, edx
786139D5   0FB656 04            movzx   edx, byte ptr
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 , edi                   ; n
786139F0   8BD9               mov   ebx, ecx                                 ; sn11 = sn_part1;
786139F2   335C24 1C            xor   ebx, dword ptr                   ; sn11 = sn_part1 ^ key3;
786139F6   8BF9               mov   edi, ecx                                 ; sn12 = sn_part1;
786139F8   035C24 20            add   ebx, dword ptr                   ; 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                   ; sn11 = sn_part2 ^ key1;
78613A12   8BF8               mov   edi, eax                                 ; sn12 = sn_part2;
78613A14   035C24 18            add   ebx, dword ptr                   ; 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                    ; 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 , 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 , cl                     ; save
78613A57   8816               mov   byte ptr , 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 , 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 , 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 , cl                     ; save
78613A7D   8856 06            mov   byte ptr , dl                     ; save
78613A80   8846 07            mov   byte ptr , al                     ; save
78613A83   5B                   pop   ebx
78613A84   59                   pop   ecx
78613A85   C3                   retn


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


退出这个函数后,回到上一级加密函数,如下图所示:

可以看到,将通过以上多个步骤加密后的SN数据,取出其中的SN、SN两个字节,并用这两个字节对注册码的前22个字节再进行一次加密(减-异或)。


从上面函数返回后,返回到SN主验证函数,如下图所示:

这里又生成一次用户名的 CRC 值(查表索引生成方式与上一次不同),取其低16位,与前面完成加密后的SN 中的 SN和SN两个字节组成的双字进行比较(0xnnnn2612 处),如果相等,则注册码有效,否则注册码无效。


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


我们修改 al == 1后(手动让注册成功),退出函数,来到下图所示位置:

这里是导出函数入口,函数名为:TRRegGetStatus_BL(),再次 F8 返回主程序,如下所示,显示注册验证调用处:

这里有多个对库函数的调用,按 F9 运行,进入软件,按”About",显示关于消息框,如下图所示:

由于前面手动改成 al==1,所以这里显示注册成功了。


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

typedef unsigned charUCHAR;
typedef unsigned short UWORD;
typedef unsigned longULONG;

/////////////
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 = (unsigned char)(crc);
      sn_char_test = (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;/// sn_intsn_int
      
      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);
      }
      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;
                } else {
                        ch_even ^= sn_char;
                }
      }

      sn_check = (ch_even<<8) + ch_odd;
      sn_check ^= ((unsigned short)key2<<8) + (unsigned short)key2;
      
      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;
      
      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;
      ch2 = key;
      ch3 = key;
      ch1 <<=8;
      ch1 += ch2;
      ch2 = key;
      ch1 <<=8;
      ch1 += ch3;
      ch3 = key;
      ch1 <<=8;
      ch1 += ch2;   /// ch1 = 0x496F6278 (7-6-5-4)
      ch2 = key;
      ch3 <<=8;
      ch3 += ch2;
      ch2 = key;
      ch3 <<=8;
      ch3 += ch2;
      ch2 = key;
      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;
      ch2 = (unsigned long)key0;
      ch3 = (unsigned long)key0;
      ch4 = (unsigned long)key0;
      ch5 = (unsigned long)key0;
      ch6 = (unsigned long)key0;
      ch1 <<= 8;
      ch1 += ch2;
      ch2 = (unsigned long)key0;
      ch4 <<= 8;
      ch4 += ch2;
      ch2 = (unsigned long)key0;
      ch1 <<= 8;
      ch1 += ch3;
      ch3 = (unsigned long)key0;
      ch6 <<= 8;
      ch4 <<= 8;
      ch6 += ch2;
      ch2 = (unsigned long)key0;
      ch1 <<= 8;
      ch1 += ch5;//// 0x9BA4B66A (17-16-15-14)
      ch5 = (unsigned long)key0;
      ch4 += ch3;
      ch3 = (unsigned long)key0;
      ch6 <<= 8;
      ch4 <<= 8;
      ch4 += ch5;//// 0xC08F9A86 (13-12-11-10)
      ch5 = (unsigned long)key0;
      ch6 += ch3;
      ch3 = (unsigned long)key0;
      ch6 <<= 8;
      ch6 += ch5;//// 0x9AA2B19E (9-8-7-6)
      ch5 = (unsigned long)key0;
      ch5 <<= 8;
      ch5 += ch2;
      ch2 = (unsigned long)key0;
      ch5 <<= 8;
      ch5 += ch3;
      ch5 <<= 8;
      ch5 += ch2;//// 0xA4DFD9AD (5-4-3-2)
      
      ////
      char * sn_ptr = &sn_int;
      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 longkey2 = 0;
      unsigned longkey3 = 0;
      unsigned longch = 0;
      unsigned longsn_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;
      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);
      ULONG sn_part2 = (ULONG)(*(ULONG *)&sn_int);
      
      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 = sn_part1;
      *(ULONG *)&sn_int = sn_part2;
      printSN(6);
      
      return 0;
}

long getSNCheck2(char * sn_int) {
      char low = sn_int; /// 0x4D
      char up= sn_int; /// 0x5F
      
      printf("up = 0x%02X, low = 0x%02X\n", up, low);
      
      for(int i=0; i<22; i++) {
                sn_int = (sn_int - 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;
}



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

戰龍在野 发表于 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

好多步驟不容易啊
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 某国外音视频转换软件之注册算法、加密算法分析及逆向注册机实现