[转自一萧烟雨]WinRAR 3.62注册算法 - CRC校验 + 黑名单 + SHA1 hash算法
标题: WinRAR 3.62注册算法 - CRC校验 + 黑名单 + SHA1 hash算法发表于: 2006-12-24 12:02 BY:icytear
--------------------------------------------------------------------------------
WinRAR 3.62注册算法 - CRC校验
[声明] 本文为逆向学习参考文章, 若使用 winrar 请向作者注册.
用WinRAR3.51的key文件rarreg.key发现被加入了黑名单, 所以分析了一下.
3.51注册key内容:
RAR registration data
Carol Thompson
Single PC usage license
UID=b8bc6fb0a8094b9eeb29
6412212250eb294bd5b605e535f7334b6e2e56a9e405a044f60225
c843a161a156aa01684c6035c6ab9048e2c5c62f0238f183d28519
aa87488bf38f5b634cf28190bdf438ac593b1857cdb55a7fcb0eb0
c3e4c2736090b3dfa45384e08e9de05c5860ae8049eaa9443b44f9
faac06b7ced5f95ab06b40a99e850616dc92fc5301fe63c674ea55
3971fefd9e10f300d2a515c74b02f673b7fe5a89fa92f51260a5af
78a306093f5763d6acc779488f5d42e9b044836a837c0424153795
1. CRC校验
使用CreateFileA 函数断点, 跟踪对文件rarreg.key文件的读取, 到以下代码:
.text:0040E6C4 check_regdata proc near
.text:0040E6C4 push ebx
.text:0040E6C5 push esi
.text:0040E6C6 push edi
.text:0040E6C7 push ebp
.text:0040E6C8 add esp, 0FFFFF178h ; data
.text:0040E6CE mov edi, eax
.text:0040E6D0 or eax, 0FFFFFFFFh ; crc32
.text:0040E6D3 mov esi, offset regData
.text:0040E6D8 mov , dl
.text:0040E6DB mov edx, offset g_HCode0 ; "70c2441db366d92ea7be1342b3bf629026ba92b"...
.text:0040E6E0 mov ecx, 40h ; len
.text:0040E6E5 call NCrc32
.text:0040E6E5
.text:0040E6EA cmp eax, 26E831B8h
.text:0040E6EF jz short HCODE0_CRC_OK ; 如果硬编码串的CRC32出错, 注册失败!
.text:0040E6EF
.text:0040E6F1 xor eax, eax
.text:0040E6F3 jmp REG_FAIL_EXIT
首先出现了一个硬编码的字符串
g_HCode0 = "70c2441db366d92ea7be1342b3bf629026ba92bb675f06e684bdd34511097434"
然后调用NCrc32计算 g_HCode0的 ~CRC32值, 比较此值是否等于0x26E831B8, 这里注册成功
的关键是WinRAR未修改g_HCode0的值.
也许你已经发现为什么函数的名字前面加了一个N, 其实查看此函数就知道, 这里计算出的结
果并非CRC32值, 而是CRC32的非, 代码如下
.text:00410734 ; DWORD __fastcall NCrc32(DWORD crc32,unsigned __int8 *buffer,DWORD len)
.text:00410734 NCrc32 proc near
.text:00410734 push ebx
.text:00410735 push esi
.text:00410736 push edi
.text:00410737 mov edi, offset crc32_table
.text:0041073C push ebp
.text:0041073D mov ebp, edx
.text:0041073F cmp dword ptr , 0
.text:00410743 mov esi, ecx
.text:00410745 mov ebx, eax
.text:00410747 jnz short CRC32TABLE_OK ; 如果CRC32表的第2个dword值为0,
.text:00410747 ; 则重新构建CRC32表crc32_table
.text:00410747
.text:00410749 call makeCrc32Table; 构建CRC32表
.text:0041074E
.text:0041074E CRC32TABLE_OK: ; CODE XREF: NCrc32+13j
.text:0041074E mov eax, ebp
.text:00410750 jmp short FOR1_condition ; FOR1的条件, buffer未处理部分长度大于0,
.text:00410750 ; 且buffer地址 8字节未对齐, 则继续循环
.text:00410752 ; ---------------------------------------------------------------------------
.text:00410752 FOR1: ; CODE XREF: NCrc32+38j
.text:00410752 mov edx, ebx ; 第一个for循环处理 buffer地址未对齐部分
.text:00410754 xor ecx, ecx
.text:00410756 xor dl,
.text:00410758 dec esi
.text:00410759 mov cl, dl
.text:0041075B shr ebx, 8
.text:0041075E mov edx,
.text:00410761 xor edx, ebx
.text:00410763 inc eax
.text:00410764 mov ebx, edx
.text:00410766
.text:00410766 FOR1_condition: ; CODE XREF: NCrc32+1Cj
.text:00410766 test esi, esi ; FOR1的条件, buffer未处理部分长度大于0,
.text:00410766 ; 且buffer地址 8字节未对齐, 则继续循环
.text:00410768 jbe short loc_41076E
.text:00410768
.text:0041076A test al, 7
.text:0041076C jnz short FOR1 ; 第一个for循环处理 buffer地址未对齐部分
.text:0041076E
.text:0041076E loc_41076E: ; CODE XREF: NCrc32+34j
.text:0041076E cmp esi, 8
.text:00410771 jb loc_4107F7 ; 判断buffer 未处理部分的长度是否大于8
.text:00410771
.text:00410777 loc_410777: ; CODE XREF: NCrc32+C1j
.text:00410777 xor ebx, ; 每次循环计算8个字节
.text:00410779 xor ecx, ecx
.text:0041077B mov cl, bl
.text:0041077D sub esi, 8
.text:00410780 shr ebx, 8
.text:00410783 mov edx,
.text:00410786 xor edx, ebx
.text:00410788 xor ecx, ecx
.text:0041078A mov ebx, edx
.text:0041078C mov cl, bl
.text:0041078E shr ebx, 8
.text:00410791 mov edx,
.text:00410794 xor edx, ebx
.text:00410796 xor ecx, ecx
.text:00410798 mov ebx, edx
.text:0041079A mov cl, bl
.text:0041079C shr ebx, 8
.text:0041079F mov edx,
.text:004107A2 xor edx, ebx
.text:004107A4 xor ecx, ecx
.text:004107A6 mov ebx, edx
.text:004107A8 mov cl, bl
.text:004107AA shr ebx, 8
.text:004107AD mov edx,
.text:004107B0 xor edx, ebx
.text:004107B2 xor ecx, ecx
.text:004107B4 mov ebx, edx
.text:004107B6 xor ebx,
.text:004107B9 add eax, 8
.text:004107BC mov cl, bl
.text:004107BE shr ebx, 8
.text:004107C1 mov edx,
.text:004107C4 xor edx, ebx
.text:004107C6 xor ecx, ecx
.text:004107C8 mov ebx, edx
.text:004107CA mov cl, bl
.text:004107CC shr ebx, 8
.text:004107CF mov edx,
.text:004107D2 xor edx, ebx
.text:004107D4 xor ecx, ecx
.text:004107D6 mov ebx, edx
.text:004107D8 mov cl, bl
.text:004107DA shr ebx, 8
.text:004107DD mov edx,
.text:004107E0 xor edx, ebx
.text:004107E2 xor ecx, ecx
.text:004107E4 mov ebx, edx
.text:004107E6 mov cl, bl
.text:004107E8 shr ebx, 8
.text:004107EB mov edx,
.text:004107EE xor edx, ebx
.text:004107F0 cmp esi, 8
.text:004107F3 mov ebx, edx
.text:004107F5 jnb short loc_410777 ; 每次循环计算8个字节
.text:004107F5
.text:004107F7 loc_4107F7: ; CODE XREF: NCrc32+3Dj
.text:004107F7 xor edx, edx
.text:004107F9 jmp short loc_410811
.text:004107FB ; ---------------------------------------------------------------------------
.text:004107FB loc_4107FB: ; CODE XREF: NCrc32+DFj
.text:004107FB mov ecx, ebx ; 处理剩余部分
.text:004107FD xor cl,
.text:00410800 and ecx, 0FFh
.text:00410806 shr ebx, 8
.text:00410809 mov ecx,
.text:0041080C xor ecx, ebx
.text:0041080E mov ebx, ecx
.text:00410810 inc edx
.text:00410811
.text:00410811 loc_410811: ; CODE XREF: NCrc32+C5j
.text:00410811 cmp esi, edx
.text:00410813 ja short loc_4107FB ; 处理剩余部分
.text:00410815 mov eax, ebx
.text:00410817 pop ebp
.text:00410818 pop edi
.text:00410819 pop esi
.text:0041081A pop ebx
.text:0041081B retn
.text:0041081B
.text:0041081B NCrc32 endp
用C简单实现了一下:
unsigned int
NCrc32 (unsigned int crc32, unsigned char *buff, unsigned int len)
{
if (crc32Table == 0)
makeCrc32Table();
for ( ; (len>0) && (((unsigned int)buff & 7)!=0); --len) // 8字节对齐
{
crc32 = crc32Table[(crc32 ^ *buff++) & 0xFF] ^ (crc32>>8);
}
// 每次计算8个字节
while (len >8)
{
crc32 ^= *(unsigned int*)buff;
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
crc32 ^= *(unsigned int*)(buff+4);
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
crc32 = crc32Table ^(crc32>>8);
len -= 8;
buff += 8;
}
// 计算剩余部分
for (unsigned int i=0; len>i; i++)
{
crc32 = crc32Table[(crc32 ^ buff) & 0xFF] ^ (crc32>>8);
}
return crc32;
}
下面的代码用到一个结构 REG_DATA:
typedef struct _reg_data
{
char usrname;
char licType;
char snCode1;
char snCode4;
char snCode3;
char snCode2;
u_int32_t crc32;
}REG_DATA;
Proxies::CreateSubClass不知道做什么的, 大概是构造子串的, 既然是库函数直接看结果了,
结果在edx指向的内存产生一个字符串, 每次都是rarreg.key文件的一行(在这之前文件的内
容已经读到内存了, 读文件部分被略了:)).
.text:0040E6F8 HCODE0_CRC_OK:
.text:0040E6F8 push 604h ; len
.text:0040E6FD push 0 ; c
.text:0040E6FF push regData ; REG_DATA
.text:0040E700 call _memset ; 结构清零
.text:0040E700
.text:0040E705 add esp, 0Ch
.text:0040E708 jmp short loc_40E715
.text:0040E70A ; ---------------------------------------------------------------------------
.text:0040E70A loc_40E70A: ; CODE XREF: check_regdata+63j
.text:0040E70A xor edx, edx
.text:0040E70C mov dl, byte ptr
.text:0040E710 cmp edx, 23h
.text:0040E713 jnz short loc_40E729
.text:0040E715 loc_40E715: ; CODE XREF: check_regdata+44j
.text:0040E715 lea edx,
.text:0040E719 mov ecx, 400h
.text:0040E71E mov eax, edi ;
.text:0040E720 call Proxies::CreateSubClass(System::TObject *,System::AnsiString,System::TMetaClass *)
.text:0040E725 test al, al
.text:0040E727 jnz short loc_40E70A
.text:0040E729
.text:0040E729 loc_40E729: ; CODE XREF: check_regdata+4Fj
.text:0040E729 mov edx, regData
.text:0040E72B mov ecx, 100h
.text:0040E730 mov eax, edi
.text:0040E732 call Proxies::CreateSubClass(System::TObject *,System::AnsiString,System::TMetaClass *)
.text:0040E737 lea edx,
.text:0040E73D mov ecx, 100h
.text:0040E742 mov eax, edi
.text:0040E744 call Proxies::CreateSubClass(System::TObject *,System::AnsiString,System::TMetaClass *)
.text:0040E749 lea edx,
.text:0040E750 mov ecx, 80h
.text:0040E755 mov eax, edi
.text:0040E757 call Proxies::CreateSubClass(System::TObject *,System::AnsiString,System::TMetaClass *)
.text:0040E75C xor ebx, ebx
.text:0040E75E
.text:0040E75E loc_40E75E: ; CODE XREF: check_regdata+B7j
.text:0040E75E imul eax, ebx, 36h
.text:0040E761 mov ecx, 400h
.text:0040E766 mov edx, eax
.text:0040E768 sub ecx, eax
.text:0040E76A lea eax,
.text:0040E76E add edx, eax
.text:0040E770 mov eax, edi
.text:0040E772 call Proxies::CreateSubClass(System::TObject *,System::AnsiString,System::TMetaClass *)
.text:0040E777 inc ebx
.text:0040E778 cmp ebx, 7
.text:0040E77B jl short loc_40E75E ;把UID行以后的, 当一串字符串(去掉CR/LF)
按字符串的前10位(snHead="6412212250" )分割字符串
.text:0040E77D lea eax,
.text:0040E781 mov edx, 2
.text:0040E786 call rar_atoi ;rar实现的把指定长度的字符串转换位整型
.text:0040E78B mov , eax
.text:0040E78F lea eax,
.text:0040E793 mov edx, 3
.text:0040E798 call rar_atoi
.text:0040E79D mov edi, eax
.text:0040E79F lea eax,
.text:0040E7A3 mov edx, 3
.text:0040E7A8 call rar_atoi
.text:0040E7AD mov ebp, eax
.text:0040E7AF lea eax,
.text:0040E7B3 mov edx, 2
.text:0040E7B8 call rar_atoi
.text:0040E7BD mov , eax
.text:0040E7C1 cmp edi, 100h ; 比较[注册字符串2]的长度
.text:0040E7C7 jg short loc_40E7D1 ;
.text:0040E7C7
.text:0040E7C9 cmp ebp, 100h ; 比较[注册字符串3]的长度
.text:0040E7CF jle short loc_40E7D8
.text:0040E7D1
.text:0040E7D1 loc_40E7D1: ; CODE XREF: check_regdata+103j
.text:0040E7D1 xor eax, eax ;
.text:0040E7D3 jmp REG_FAIL_EXIT ; 如果 [注册字符串2], [注册字符串3] 有一个长度大于 256,
.text:0040E7D3 ; 注册失败!
.text:0040E7D8 ; ---------------------------------------------------------------------------
[跳过一段代码, 这段代码是检查黑名单, 稍后讲]
下面这部分是按照snHead="6412212250", 把字符串分割后拷贝到regData结构中, 分割方法
就是按上面的 2, 3, 3, 2, 即64, 122, 122, 50(十进制)长分割.
.text:0040E882 loc_40E882: ; CODE XREF: check_regdata+1A5j
.text:0040E882 ; check_regdata+1B5j
.text:0040E882 inc ebx
.text:0040E883 cmp ebx, 6
.text:0040E886 jl short NEXT_UBLACK
.text:0040E888 lea ebx,
.text:0040E88C push ; snCode1_len
.text:0040E890 push ebx ; src
.text:0040E891 lea eax,
.text:0040E897 push eax ; dest
.text:0040E898 call _strncpy
.text:0040E89D add esp, 0Ch
.text:0040E8A0 add ebx,
.text:0040E8A4 push edi ; snCode2_len
.text:0040E8A5 push ebx ; src
.text:0040E8A6 lea edx,
.text:0040E8AC push edx ; dest
.text:0040E8AD call _strncpy
.text:0040E8B2 add esp, 0Ch
.text:0040E8B5 add ebx, edi
.text:0040E8B7 push ebp ; snCode3_len
.text:0040E8B8 push ebx ; src
.text:0040E8B9 lea ecx,
.text:0040E8BF push ecx ; dest
.text:0040E8C0 call _strncpy
.text:0040E8C5 add esp, 0Ch
.text:0040E8C8 add ebx, ebp
.text:0040E8CA push ; maxlen
.text:0040E8CE push ebx ; src
.text:0040E8CF lea eax,
.text:0040E8D5 push eax ; dest
.text:0040E8D6 call _strncpy
.text:0040E8DB add esp, 0Ch
.text:0040E8DE add ebx,
.text:0040E8E2 push 0Ah ; radix
.text:0040E8E4 push 0 ; endptr
.text:0040E8E6 push ebx ; s
.text:0040E8E7 call _strtoul ; 把最后10位转换作为CRC32
.text:0040E8EC add esp, 0Ch
.text:0040E8EF mov , eax
分割后各部分数据如下:
regHead = "RAR registration data"
regData.username = "Carol Thompson" //
regData.licType= "Single PC usage license" // regData.licType 注册类型
regUid = "UID=b8bc6fb0a8094b9eeb29" //
snHead = "6412212250" // [注册字符串头] len = 10
regData.snCode1 = "eb294bd5b605e535f7334b6e2e56a9e405a044f60225c843a161a156aa01684c" // [注册字符串1] len = 64
regData.snCode2= "6035c6ab9048e2c5c62f0238f183d28519aa87488bf38f5b634cf28190bdf438ac593b1857cdb55a7fcb0eb0c3e4c2736090b3dfa45384e08e9de05c58" // [注册字符串2] len = 122
regData.snCode3= "60ae8049eaa9443b44f9faac06b7ced5f95ab06b40a99e850616dc92fc5301fe63c674ea553971fefd9e10f300d2a515c74b02f673b7fe5a89fa92f512" // [注册字符串3] len = 122
regData.snCode4= "60a5af78a306093f5763d6acc779488f5d42e9b044836a837c", // [注册字符串4] len = 50
regData.crc32 = "0424153795"; // [头和1,2,3部分的 NCRC32值(10进制)] len = 10
下面代码是计算 regData.licType + username + regData.snCode1 + .snCode2 + .snCode3 + .snCode4
的~CRC32值, 判断是否等于regData.crc32, 不等注册失败!
.text:0040E8F5 lea edx,
.text:0040E8FB push edx ; s
.text:0040E8FC call _strlen
.text:0040E901 pop ecx
.text:0040E902 mov ecx, eax ; len
.text:0040E904 lea edx, ; buffer
.text:0040E90A or eax, 0FFFFFFFFh ; crc32
.text:0040E90D call NCrc32
.text:0040E912 mov ebx, eax
.text:0040E914 push regData ; s
.text:0040E915 call _strlen
.text:0040E91A pop ecx
.text:0040E91B mov ecx, eax ; len
.text:0040E91D mov edx, regData ; buffer
.text:0040E91F mov eax, ebx ; crc32
.text:0040E921 call NCrc32
.text:0040E926 mov ebx, eax
.text:0040E928 lea eax,
.text:0040E92E push eax ; s
.text:0040E92F call _strlen
.text:0040E934 pop ecx
.text:0040E935 mov ecx, eax ; len
.text:0040E937 lea edx, ; buffer
.text:0040E93D mov eax, ebx ; crc32
.text:0040E93F call NCrc32
.text:0040E944 mov ebx, eax
.text:0040E946 lea edx,
.text:0040E94C push edx ; s
.text:0040E94D call _strlen
.text:0040E952 pop ecx
.text:0040E953 mov ecx, eax ; len
.text:0040E955 lea edx, ; buffer
.text:0040E95B mov eax, ebx ; crc32
.text:0040E95D call NCrc32
.text:0040E962 mov ebx, eax
.text:0040E964 lea ecx,
.text:0040E96A push ecx ; s
.text:0040E96B call _strlen
.text:0040E970 pop ecx
.text:0040E971 mov ecx, eax ; len
.text:0040E973 lea edx, ; buffer
.text:0040E979 mov eax, ebx ; crc32
.text:0040E97B call NCrc32
.text:0040E980 mov ebx, eax
.text:0040E982 lea eax,
.text:0040E988 push eax ; s
.text:0040E989 call _strlen
.text:0040E98E pop ecx
.text:0040E98F mov ecx, eax ; len
.text:0040E991 lea edx, ; buffer
.text:0040E997 mov eax, ebx ; crc32
.text:0040E999 call NCrc32
.text:0040E99E mov ebx, eax
.text:0040E9A0 cmp ebx, ; licType + username + snCode1/2/3/4 的~CRC32值,
; 和regData.crc32比较, 不相等注册失败!
.text:0040E9A6 jz short loc_40E9AF
.text:0040E9A8 xor eax, eax
.text:0040E9AA jmp REG_FAIL_EXIT
.text:0040E9AF ; ---------------------------------------------------------------------------
到此基本了解了文件的rarreg.key的结构, 下一篇介绍黑名单部分.
牛人的一个算法。。。 非一般的牛人啊...
≡ 一 蓑 烟 雨 ≡ 哦... 好强啊,谢谢 真是老牛了/find 哎,这个更加晕了,
现在WINRAR越来越厉害了,,,,,,,深 x谢谢分享致辞了,呵呵 谢谢分享支持支持!!! 真是强,望尘莫及哦,资料收藏了,以后慢慢学 厉害,看得不是很白,
页:
[1]
2