【文章标题】: IMToo系列软件的算法分析
【软件名称】: IMToo系列软件
【详细过程】
同样是一个通过专用DLL来验证程序,通过在DLL里查看导出函数,很快就能找到:
UILib8_M.ImRegUserInfo::IsValidRegInfo_private
双击来到段着下断:
0046D040 UILib8>/$ 6A FF push -1
0046D042 |. 68 BB914A00 push UILib8_M.004A91BB ; SE 句柄安装
0046D047 |. 64:A1 00000000 mov eax,dword ptr fs:[0]
0046D04D |. 50 push eax
0046D04E |. 64:8925 00000000 mov dword ptr fs:[0],esp
0046D055 |. 81EC B8000000 sub esp,0B8
经过一段注册表的读取过程,很快来到这里:
0046D225 |> \6A 14 push 14 ; /Arg2 = 00000014
0046D227 |. 8D4C24 0C lea ecx,dword ptr ss:[esp+C] ; |
0046D22B |. 51 push ecx ; |Arg1 = 0013E8AC
0046D22C |. 8D4C24 18 lea ecx,dword ptr ss:[esp+18] ; |取输入的假码前20位,设为:szConst
0046D230 |. FF15 C8E54A00 call dword ptr ds:[<&MFC71U.#3990>] ; \MFC71U.7C29977E
继续单步,就看到了对注册码的长度的比较:
0046D27F |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10]
0046D283 |. FF15 8CE54A00 call dword ptr ds:[<&MFC71U.#2895>] ; MFC71U.7C256550
0046D289 |. 83F8 27 cmp eax,27 ; 将注册码长度与0x27比较
0046D28C |. 0F85 D3030000 jnz UILib8_M.0046D665 ; 不等则失败
0046D2D6 |. 50 push eax ; 出现字符:ImTOOavimpegconverter5
0046D2D7 |. 6A 00 push 0 ; 我们记为:sName
0046D2D9 |. E8 824B0000 call UILib8_M.CImTools::WC_A
继续单步吧,很快就来到了注册算法的关键部分了,呵呵
0046D343 |. 8B4C24 20 mov ecx,dword ptr ss:[esp+20] ; 将ECX指向字符串
0046D347 |. 8B41 F4 mov eax,dword ptr ds:[ecx-C] ; 将字符串的长度存储到EAX
0046D34A |. 33F6 xor esi,esi
0046D34C |. 85C0 test eax,eax
0046D34E |. C68424 C8000000 0>mov byte ptr ss:[esp+C8],9
0046D356 |. 7E 5D jle short UILib8_M.0046D3B5
0046D358 |. EB 06 jmp short UILib8_M.0046D360
0046D35A | 8D9B 00000000 lea ebx,dword ptr ds:[ebx]
0046D360 |> 8BD6 /mov edx,esi
0046D362 |. 81E2 01000080 |and edx,80000001
0046D368 |. 79 05 |jns short UILib8_M.0046D36F
0046D36A |. 4A |dec edx
0046D36B |. 83CA FE |or edx,FFFFFFFE
0046D36E |. 42 |inc edx
0046D36F |> 75 38 |jnz short UILib8_M.0046D3A9 ; 测试EDX是否为0
0046D371 |. 56 |push esi
0046D372 |. 8D4C24 24 |lea ecx,dword ptr ss:[esp+24]
0046D376 |. FF15 7CE34A00 |call dword ptr ds:[<&MFC71U.#861>] ; MFC71U.7C29986D
0046D37C |. 8D4C24 0C |lea ecx,dword ptr ss:[esp+C]
0046D380 |. 50 |push eax
0046D381 |. FF15 44E34A00 |call dword ptr ds:[<&MFC71U.#904>] ; MFC71U.7C29B289
0046D387 |. 8D46 01 |lea eax,dword ptr ds:[esi+1] ; 将ESI+1的结果保存到EAX
0046D38A |. 99 |cdq
0046D38B |. B9 FF000000 |mov ecx,0FF
0046D390 |. F7F9 |idiv ecx ; 然后除以0xFF
0046D392 |. 84D2 |test dl,dl ; 比较余数不否为0
0046D394 |. 885424 08 |mov byte ptr ss:[esp+8],dl
0046D398 |. 74 0F |je short UILib8_M.0046D3A9 ; 为0则跳转,否则保存到ESP+8的位置
0046D39A |. 8B5424 08 |mov edx,dword ptr ss:[esp+8]
0046D39E |. 52 |push edx
0046D39F |. 8D4C24 10 |lea ecx,dword ptr ss:[esp+10]
0046D3A3 |. FF15 44E34A00 |call dword ptr ds:[<&MFC71U.#904>] ; MFC71U.7C29B289
0046D3A9 |> 8B4C24 20 |mov ecx,dword ptr ss:[esp+20]
0046D3AD |. 8B41 F4 |mov eax,dword ptr ds:[ecx-C]
0046D3B0 |. 46 |inc esi
0046D3B1 |. 3BF0 |cmp esi,eax
0046D3B3 |.^ 7C AB \jl short UILib8_M.0046D360
0046D3B5 |> 8B41 F4 mov eax,dword ptr ds:[ecx-C]
用代码来表示如下:
len=strlen(sName);
int num1[32]={0};
int num1len=0;
for(int i=0;iint tmp=i;
tmp=tmp & 0x80000001;
if(tmp==0){
tmp=(i+1) % 0xFF;
if(tmp!=0){
num1[num1len]=tmp;
num1len++;
}
}
}
下面还有一段相同的,操作相反的代码(取偶数位,上面的是取奇数位):
0046D3BE |. 8BFF mov edi,edi
0046D3C0 |> 8BC6 /mov eax,esi
0046D3C2 |. 25 01000080 |and eax,80000001
0046D3C7 |. 79 05 |jns short UILib8_M.0046D3CE
0046D3C9 |. 48 |dec eax
0046D3CA |. 83C8 FE |or eax,FFFFFFFE
0046D3CD |. 40 |inc eax
0046D3CE |> 74 38 |je short UILib8_M.0046D408
0046D3D0 |. 56 |push esi
0046D3D1 |. 8D4C24 24 |lea ecx,dword ptr ss:[esp+24]
0046D3D5 |. FF15 7CE34A00 |call dword ptr ds:[<&MFC71U.#861>] ; MFC71U.7C29986D
0046D3DB |. 8D4C24 0C |lea ecx,dword ptr ss:[esp+C]
0046D3DF |. 50 |push eax
0046D3E0 |. FF15 44E34A00 |call dword ptr ds:[<&MFC71U.#904>] ; MFC71U.7C29B289
0046D3E6 |. 8D46 01 |lea eax,dword ptr ds:[esi+1]
0046D3E9 |. 99 |cdq
0046D3EA |. B9 FF000000 |mov ecx,0FF
0046D3EF |. F7F9 |idiv ecx
0046D3F1 |. 84D2 |test dl,dl
0046D3F3 |. 885424 08 |mov byte ptr ss:[esp+8],dl
0046D3F7 |. 74 0F |je short UILib8_M.0046D408
0046D3F9 |. 8B5424 08 |mov edx,dword ptr ss:[esp+8]
0046D3FD |. 52 |push edx
0046D3FE |. 8D4C24 10 |lea ecx,dword ptr ss:[esp+10]
0046D402 |. FF15 44E34A00 |call dword ptr ds:[<&MFC71U.#904>] ; MFC71U.7C29B289
0046D408 |> 8B4424 20 |mov eax,dword ptr ss:[esp+20]
0046D40C |. 8B48 F4 |mov ecx,dword ptr ds:[eax-C]
0046D40F |. 46 |inc esi
0046D410 |. 3BF1 |cmp esi,ecx
0046D412 |.^ 7C AC \jl short UILib8_M.0046D3C0 ; 与上一个循环进行相反的操作
0046D414 |> 8D4C24 2C lea ecx,dword ptr ss:[esp+2C]
同样用代码表示如下:
int num2[32]={0};
int num2len=0;
for(int i=0;iint tmp=i;
tmp=tmp & 0x80000001;
if(tmp!=0){
tmp=(i+1) % 0xFF;
if(tmp!=0){
num2[num2len]=tmp;
num2len++;
}
}
}
接下来就是对关键字符串的合成了:
…………
0046D566 |. 52 push edx
0046D567 |. 8D4424 2C lea eax,dword ptr ss:[esp+2C]
0046D56B |. 50 push eax
0046D56C |. 8D4C24 1C lea ecx,dword ptr ss:[esp+1C]
0046D570 |. 51 push ecx
0046D571 |. E8 6AF8FFFF call UILib8_M.0046CDE0 ; 合成字符
0046D576 |. 83C4 18 add esp,18
0046D579 |. 50 push eax
0046D57A |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10]
0046D57E |. C68424 CC000000 1>mov byte ptr ss:[esp+CC],11
0046D586 |. FF15 34E34A00 call dword ptr ds:[<&MFC71U.#903>] ; MFC71U.7C29B383
0046D58C |. 8D4C24 08 lea ecx,dword ptr ss:[esp+8]
0046D590 |. 889C24 C8000000 mov byte ptr ss:[esp+C8],bl
0046D597 |. FF15 1CE54A00 call dword ptr ds:[<&MFC71U.#578>] ; MFC71U.7C2868E9
通过观察与分析,首先是将上面两个大循环得到的结果,与软件名位错位相连,然后嫁接两个0x30,
再把假码的前20位与软件名连接到上面。首位不变,是0x31!
strCode[0]=0x31;
int k=1,i=0,j=0;
while(jstrCode[k]=sName[i];
i+=2;
strCode[k+1]=num1[j];
j++;
k+=2;
}
j=0,i=1;
while(jstrCode[k]=sName[i];
i+=2;
strCode[k+1]=num2[j];
j++;
k+=2;
}
strCode[k]=0x30;
k++;
strCode[k]=0x30;
k++;
TCHAR szConst[]="11111A22222B33333C44";
for(int i=0;i<20;i++,k++)
strCode[k]=szConst[i];
for(int i=0;istrCode[k]=sName[i];
观察数据窗口:
03FC4018 31 49 01 54 03 4F 05 76 07 6D 09 65 0B 63 0D 6E 1ITOvm.ec.n
03FC4028 0F 65 11 74 13 72 15 6D 02 4F 04 61 06 69 08 70 etrmOaip
03FC4038 0A 67 0C 6F 0E 76 10 72 12 65 14 35 16 30 30 31 .g.ovre5001
03FC4048 31 31 31 31 41 32 32 32 32 32 42 33 33 33 33 33 1111A22222B33333
03FC4058 43 34 34 49 6D 54 4F 4F 61 76 69 6D 70 65 67 63 C44ImTOOavimpegc
03FC4068 6F 6E 76 65 72 74 65 72 35 onverter5
然后对上面的字符串进行MD5计算:
0046D5A1 |. 52 push edx ; /Arg1
0046D5A2 |. 8D8C24 84000000 lea ecx,dword ptr ss:[esp+84] ; |
0046D5A9 |. E8 12E40200 call UILib8_M.0049B9C0 ; \UILib8_M.0049B9C0
接下来,继续对得到的MD5字符串,进行处理:
0046D6B4 |> /56 /push esi
0046D6B5 |. |8D4C24 34 |lea ecx,dword ptr ss:[esp+34]
0046D6B9 |. |FF15 7CE34A00 |call dword ptr ds:[<&MFC71U.#861>] ; MFC71U.7C29986D
0046D6BF |. |8D4C24 0C |lea ecx,dword ptr ss:[esp+C]
0046D6C3 |. |50 |push eax
0046D6C4 |. |FF15 44E34A00 |call dword ptr ds:[<&MFC71U.#904>] ; MFC71U.7C29B289
0046D6CA |. |8BCE |mov ecx,esi
0046D6CC |. |D1E9 |shr ecx,1 ; <<1,将最低位移到CF中
0046D6CE |. |41 |inc ecx
0046D6CF |. |81E1 03000080 |and ecx,80000003
0046D6D5 |. |79 05 |jns short UILib8_M.0046D6DC
0046D6D7 |. |49 |dec ecx
0046D6D8 |. |83C9 FC |or ecx,FFFFFFFC
0046D6DB |. |41 |inc ecx
0046D6DC |> |75 0F |jnz short UILib8_M.0046D6ED
0046D6DE |. |68 38F94A00 |push UILib8_M.004AF938
0046D6E3 |. |8D4C24 10 |lea ecx,dword ptr ss:[esp+10]
0046D6E7 |. |FF15 60E34A00 |call dword ptr ds:[<&MFC71U.#907>] ; MFC71U.7C29B867
0046D6ED |> |83C6 02 |add esi,2
0046D6F0 |. |83FE 20 |cmp esi,20
0046D6F3 |.^\7C BF \jl short UILib8_M.0046D6B4
0046D6F5 |. 8D4C24 0C lea ecx,dword ptr ss:[esp+C] ; 对MD5值的字符串进行处理
0046D6F9 |. FF15 74E34A00 call dword ptr ds:[<&MFC71U.#4079>] ; 隔位取,4位一组,以-相连
0046D706 |. 6A 01 push 1
0046D708 |. 48 dec eax
0046D709 |. 50 push eax
0046D70A |. 8D4C24 14 lea ecx,dword ptr ss:[esp+14] ; 去掉最后多出的一个“-”
将处理得到的字符串嫁接到假码的前20位后面,与假码进行比较,然后就开始设置标志位了,呵呵!
0046D718 |. 50 push eax
0046D719 |. 6A 00 push 0
0046D71B |. 8D4C24 14 lea ecx,dword ptr ss:[esp+14]
0046D71F |. FF15 78E34A00 call dword ptr ds:[<&MFC71U.#3844>] ; MFC71U.7C299F0A
0046D725 |. 8B4C24 34 mov ecx,dword ptr ss:[esp+34] ; 将上面提所得字符串转大写
0046D729 |. 51 push ecx ; 与假码的前20位相连
0046D72A |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10] ; 然后与假码比较
0046D72E |. FF15 54E34A00 call dword ptr ds:[<&MFC71U.#1473>] ; MFC71U.7C29927C
0046D734 |. F7D8 neg eax
0046D736 |. 1AC0 sbb al,al
0046D738 |. FEC0 inc al
完整的VC代码如下(这里将前20位固定下来了,没有模拟随机生成!):
void RegistMyCrackMe(HWND hDlg){
TCHAR sName[STR_LEN]={0};
memset(sName,0,STR_LEN);
int len=GetDlgItemText(hDlg,IDC_SNAME,sName,STR_LEN);
if(len==0){
MessageBox(hDlg,TEXT("请按照说明输入软件名称!用户名随意"),TEXT("提示"),0);
return;
}
//计算用户名的MD5散列
MD5_CTX context;
TCHAR szHash[STR_LEN]={0};
TCHAR szBuffer[STR_LEN]={0};
memset(szBuffer,0,STR_LEN);
memset(strCode,0,STR_LEN);
len=strlen(sName);
int num1[32]={0};
int num1len=0;
for(int i=0;iint tmp=i;
tmp=tmp & 0x80000001;
if(tmp==0){
tmp=(i+1) % 0xFF;
if(tmp!=0){
num1[num1len]=tmp;
num1len++;
}
}
}
int num2[32]={0};
int num2len=0;
for(int i=0;iint tmp=i;
tmp=tmp & 0x80000001;
if(tmp!=0){
tmp=(i+1) % 0xFF;
if(tmp!=0){
num2[num2len]=tmp;
num2len++;
}
}
}
strCode[0]=0x31;
int k=1,i=0,j=0;
while(jstrCode[k]=sName[i];
i+=2;
strCode[k+1]=num1[j];
j++;
k+=2;
}
j=0,i=1;
while(jstrCode[k]=sName[i];
i+=2;
strCode[k+1]=num2[j];
j++;
k+=2;
}
strCode[k]=0x30;
k++;
strCode[k]=0x30;
k++;
TCHAR szConst[]="11111A22222B33333C44";
for(int i=0;i<20;i++,k++)
strCode[k]=szConst[i];
for(int i=0;istrCode[k]=sName[i];
len=strlen(strCode);
MD5Init(&context);
MD5Update(&context,(unsigned char*)strCode, len);
MD5Final((unsigned char*)szHash, &context);
for(int i=0; i < 16; i++) // 将szHash[]中的16进制转换成字符形式显示
wsprintf(&szBuffer[i*2], "%02X", *(byte*)(szHash+i));
memset(strCode,0,STR_LEN);
k=0;
for(int i=0;i<20;i++,k++)
strCode[k]=szConst[i];
j=0;
for(int i=0;i<32;i+=2){
strCode[k]=szBuffer[i];
j++;
if((j%4)==0){
k++;
strCode[k]='-';
}
k++;
}
strCode[k-1]=0;//去掉最后一个'-1'
SetDlgItemText(hDlg,IDC_PWD,strCode);
return;
}
——————————————————————————–
【版权声明】: 转载请注明作者并保持文章的完整, 谢谢!
2009年01月15日 15:47:11