某国外音视频转换软件之注册算法、加密算法分析及逆向注册机实现
本帖最后由 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 于 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 于 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;
}
第二部分就到这里,后面第三部分是对上面的代码进行简化和优化,这样方便分析其算法,逆向出注册机代码。 分析得很好謝謝提供學習了 看着这个软件转换能力还蛮强的 期待试用 哈哈 感谢分享 辛苦了! 我用这家呢视频分割合并工具比较多 真是高深莫测呀,这回有的耍了,感谢楼主技术分享 好多步驟不容易啊