【文章标题】Internet Download Manager 算法分析
【文章作者】BinGzL
【软件版本】Internet Download Manager v6.23 Build17
【保护方式】序列号
【分析过程】
本文不阐述定位算法的过程,通过调试定位如下函数(看官可略过,看下面的分析):
刚进入函数就看到了获取注册信息的部分与初始化一张Key表
获取名字
.text:004FFB31 lea eax, [ebp+szName]
.text:004FFB3A push 32h ; cchMax
.text:004FFB3E push eax ; lpString
.text:004FFB3F push 4B0h ; nIDDlgItem
.text:004FFC4A call GetRegUserInfo
获取姓氏
.text:004FFC78 lea edx, [ebp+szSurName]
.text:004FFC7E push 32h ; cchMax
.text:004FFC80 push edx ; lpString
.text:004FFC81 push 413h ; nIDDlgItem
.text:004FFC88 call GetRegUserInfo
获取EMail
.text:004FFC9F lea ecx, [ebp+szEmail]
.text:004FFCA5 push 32h ; cchMax
.text:004FFCA7 push ecx ; lpString
.text:004FFCA8 push 4A5h ; nIDDlgItem
.text:004FFCAF call GetRegUserInfo
获取序列号
.text:004FFCC7 lea eax, [ebp+szSerialNum]
.text:004FFCCD push 32h ; cchMax
.text:004FFCCF push eax ; lpString
.text:004FFCD0 push 4AAh ; nIDDlgItem
.text:004FFCD7 call GetRegUserInfo
.text:005E30A4 ; int __stdcall GetRegUserInfo(int nIDDlgItem, LPSTR lpString, int cchMax)
.text:005E30A4 GetRegUserInfo proc near ; CODE XREF: IsRegister+13Ap
.text:005E30A4 ; IsRegister+178p ...
.text:005E30A4
.text:005E30A4 nIDDlgItem = dword ptr 8
.text:005E30A4 lpString = dword ptr 0Ch
.text:005E30A4 cchMax = dword ptr 10h
.text:005E30A4
.text:005E30A4 push ebp
.text:005E30A5 mov ebp, esp
.text:005E30A7 mov eax, [ecx+34h]
.text:005E30AA test eax, eax
.text:005E30AC jnz short loc_5E30C2
.text:005E30AE push [ebp+cchMax] ; cchMax
.text:005E30B1 push [ebp+lpString] ; lpString
.text:005E30B4 push [ebp+nIDDlgItem] ; nIDDlgItem
.text:005E30B7 push dword ptr [ecx+1Ch] ; hDlg
.text:005E30BA call ds:GetDlgItemTextA
.text:005E30C0 jmp short loc_5E30D2
.text:005E30C2 ; ---------------------------------------------------------------------------
.text:005E30C2
.text:005E30C2 loc_5E30C2: ; CODE XREF: GetRegUserInfo+8j
.text:005E30C2 push [ebp+cchMax]
.text:005E30C5 mov edx, [eax]
.text:005E30C7 mov ecx, eax
.text:005E30C9 push [ebp+lpString]
.text:005E30CC push [ebp+nIDDlgItem]
.text:005E30CF call dword ptr [edx+78h]
.text:005E30D2
.text:005E30D2 loc_5E30D2: ; CODE XREF: GetRegUserInfo+1Cj
.text:005E30D2 pop ebp
.text:005E30D3 retn 0Ch
.text:005E30D3 GetRegUserInfo endp
Key表内容为:
char g_KeyTable[] =
{
0x32, 0x59, 0x4f, 0x50, 0x42, 0x33,
0x41, 0x51, 0x43, 0x56, 0x55, 0x58,
0x4d, 0x4e, 0x52, 0x53, 0x39, 0x37,
0x57, 0x45, 0x30, 0x49, 0x5a, 0x44,
0x34, 0x4b, 0x4c, 0x46, 0x47, 0x48,
0x4a, 0x38, 0x31, 0x36, 0x35, 0x54
};
检查序列号是否为空
.text:004FFCF1 mov dl, ' ' ; 0x20 == ' '
.text:004FFCF3 cmp [ebp+szSerialNum], dl
.text:004FFCF9 jnz short loc_4FFD57 ;
序列号转换与检查
.text:004FFDAD lea edx, [ebp+szSerialNum]
.text:004FFDB3 push edx ; char *
.text:004FFDB4 call __strupr ; 序列号转换大写
.text:004FFDB9 lea edi, [ebp+szSerialNum]
.text:004FFDBF or ecx, 0FFFFFFFFh
.text:004FFDC2 xor eax, eax
.text:004FFDC4 add esp, 4
.text:004FFDC7 repne scasb
.text:004FFDC9 not ecx
.text:004FFDCB dec ecx
.text:004FFDCC cmp ecx, 23 ; 比较序列号是否为23位
.text:004FFDCF jnz short loc_4FFD69
.text:004FFDD1 mov cl, [ebp+szSerialNum+5]
.text:004FFDD4 mov byte ptr [ebp+var_14+3], al ; bSerialNumLog == FALSE
.text:004FFDD7 mov al, 2Dh
.text:004FFDD9 cmp cl, al ; 比较序列号第6位是否为'-'(0x2d)
.text:004FFDDB jnz short loc_4FFDE7 ; bSerialNumLog == TRUE
.text:004FFDDD cmp [ebp+szSerialNum+0Bh], al ; 比较序列号第12位是否为'-'(0x2d)
.text:004FFDE0 jnz short loc_4FFDE7 ; bSerialNumLog == TRUE
.text:004FFDE2 cmp [ebp+szSerialNum+11h], al ; 比较序列号第18位是否为'-'(0x2d)
.text:004FFDE5 jz short loc_4FFDEB
由此可以确定系列号的格式为XXXXX-XXXXX-XXXXX-XXXXX,接下来又把4段系列号分别拷贝出来
.text:004FFDEB lea eax, [ebp+szSerialNum]
.text:004FFDF1 push 5 ; size_t
.text:004FFDF3 lea ecx, [ebp+szSerialNum_0_4]
.text:004FFDF6 push eax ; char *
.text:004FFDF7 push ecx ; char *
.text:004FFDF8 call _strncpy ; 获取0-4位的序列号
.text:004FFDFD lea edx, [ebp+szSerialNum+6]
.text:004FFE00 push 5 ; size_t
.text:004FFE02 lea eax, [ebp+szSerialNum_6_10]
.text:004FFE05 push edx ; char *
.text:004FFE06 push eax ; char *
.text:004FFE07 call _strncpy ; 获取6-10位序列号
.text:004FFE0C lea ecx, [ebp+szSerialNum+0Ch]
.text:004FFE0F push 5 ; size_t
.text:004FFE11 lea edx, [ebp+szSerialNum_12_16]
.text:004FFE14 push ecx ; char *
.text:004FFE15 push edx ; char *
.text:004FFE16 call _strncpy ; 获取12-16位序列号
.text:004FFE1B lea eax, [ebp+szSerialNum+12h]
.text:004FFE1E push 5 ; size_t
.text:004FFE20 lea ecx, [ebp+szSerialNum_18_22]
.text:004FFE23 push eax ; char *
.text:004FFE24 push ecx ; char *
.text:004FFE25 call _strncpy ; 获取18-22位序列号
.text:004FFE2A xor edi, edi
.text:004FFE2C add esp, 30h
.text:004FFE2F mov [ebp+szSerialNum_0_4+5], 0 ; 为拷贝出来的每段加'\0'
.text:004FFE33 mov [ebp+szSerialNum_6_10+5], 0
.text:004FFE37 mov [ebp+szSerialNum_12_16+5], 0
.text:004FFE3B mov [ebp+szSerialNum_18_22+5], 0 ;
算法的准备工作就算完成了,接下来出现了四段一样的算法,如下
.text:004FFE3F mov [ebp+dwResult0_4], edi
.text:004FFE42 xor esi, esi
.text:004FFE44
.text:004FFE44 loc_4FFE44: ; CODE XREF: IsRegister+362j
.text:004FFE44 cmp esi, 5
.text:004FFE47 jge short loc_4FFE7B ; nData = 0
.text:004FFE49 mov dl, [ebp+esi+szSerialNum_0_4]
.text:004FFE4D or ecx, 0FFFFFFFFh
.text:004FFE50 xor eax, eax
.text:004FFE52
.text:004FFE52 loc_4FFE52: ; CODE XREF: IsRegister+365j
.text:004FFE52 cmp eax, 36 ; KeyTable Len
.text:004FFE55 jge short loc_4FFE61 ;
.text:004FFE55 ; ;
.text:004FFE57 cmp g_szKeyTable[eax], dl
.text:004FFE5D jnz short loc_4FFE74
.text:004FFE5F mov ecx, eax ; nIndex = j
.text:004FFE61
.text:004FFE61 loc_4FFE61: ; CODE XREF: IsRegister+345j
.text:004FFE61 cmp ecx, 0FFFFFFFFh
.text:004FFE64 jz short loc_4FFE77
.text:004FFE66 lea edx, [edi+edi*8]
.text:004FFE69 add ecx, edi
.text:004FFE6B inc esi
.text:004FFE6C lea edi, [ecx+edx*4] ; nData = nIndex + dwResult0_4 + (dwResult0_4 * 9) * 4;
.text:004FFE6F mov [ebp+dwResult0_4], edi ; dwResult = nData
.text:004FFE72 jmp short loc_4FFE44
.text:004FFE74 ; ---------------------------------------------------------------------------
.text:004FFE74
.text:004FFE74 loc_4FFE74: ; CODE XREF: IsRegister+34Dj
.text:004FFE74 inc eax
.text:004FFE75 jmp short loc_4FFE52 ; KeyTable Len
.text:004FFE77 ; ---------------------------------------------------------------------------
.text:004FFE77
.text:004FFE77 loc_4FFE77: ; CODE XREF: IsRegister+354j
.text:004FFE77 mov byte ptr [ebp+var_14+3], 1
分析过后得出如下代码:
int CIDMKeyGenDlg::GetCalcResult(char *pData)
{
int i = 0, j = 0;
int nIndex = 0;
int nData = 0;
while (i < 5)
{
for (j = 0; j < 36; j++)
{
if (g_KeyTable[j] == pData[i])
{
nIndex = j;
nData = nIndex + nData + (nData * 9) * 4;
i++;
break;
}
}
}
return nData;
}
接下来在得到4段序列号计算出的值后,分别有对四个值做了校验
第一段
.text:004FFF42 mov ecx, [ebp+dwResult0_4]
.text:004FFF45 mov esi, 43
.text:004FFF4A mov eax, ecx
.text:004FFF4C cdq
.text:004FFF4D idiv esi
.text:004FFF4F test edx, edx
.text:004FFF51 jnz short loc_4FFF57
第二段
.text:004FFF5B mov ecx, dword ptr [ebp+dwResult6_10]
.text:004FFF5E mov esi, 23
.text:004FFF63 mov eax, ecx
.text:004FFF65 cdq
.text:004FFF66 idiv esi
.text:004FFF68 test edx, edx
.text:004FFF6A jnz short loc_4FFF70
第三段
.text:004FFF74 mov eax, ebx
.text:004FFF76 mov ecx, 17
.text:004FFF7B cdq
.text:004FFF7C idiv ecx
.text:004FFF7E test edx, edx
.text:004FFF80 jnz short loc_4FFF86
第四段
.text:004FFF8A mov eax, edi
.text:004FFF8C mov ecx, 53
.text:004FFF91 cdq
.text:004FFF92 idiv ecx
.text:004FFF94 test edx, edx
.text:004FFF96 jnz short loc_4FFFA3
剩下的就是保存注册信息的代码了,由此可以了解软件注册的验证流程为4段计算的值都可以整除一个值才可以注册。本以为到此结束....第二天用的时候打开弹出了让我心碎的提示
再次分析启动验证,发现是有网络验证,看来是不能够愉快的KeyGen了,这KeyGen自然也就是然并卵了。通杀补丁可以去飘云阁找飘哥的那份补丁。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)