题目:Winamp Skin Maker注册码分析
软件介绍:制作WinAmp的皮肤的一个小软件,可以用你喜欢的各种图片制作皮肤,设置各种效果。如果不注册,就在你制作的皮肤上显示“Unregistered

”字样,注册费$15.
目的:分析注册码算法逻辑,写个小注册机。
难度:Very Easy
工具:Softice ,PEiD
引子:今天又在光盘上找到这个小软件,来自电脑爱好者2002合订本,纯粹拿来解闷,没有想到太简单了,几分钟就搞定它,但我还是把分析过程稍微一

说吧。不要扔鸡蛋过来哟!!:)

用PEiD查看是否加壳,结果没有,是VC编写的,启动程序,打开Help菜单,点击里面的About,在弹出的对话框内,点击Register按钮,输入用户名和假注

册码,比如 wanggang,78787878。在SoftICE内下断点bpx hmemcpy,F5退出,点击OK按钮,被断下。按9次F12来到主程序代码处:
1.程序总体布局
00406346   . 8D4424 28      LEA EAX,DWORD PTR SS:[ESP+28]
0040634A   . 6A 1E          PUSH 1E
0040634C   . 50             PUSH EAX
0040634D   . 68 FD030000    PUSH 3FD
00406352   . 56             PUSH ESI
00406353   . FFD7           CALL EDI        //取注册码长度
00406355   . 8D4424 08      LEA EAX,DWORD PTR SS:[ESP+8]   //用户名地址送EAX
00406359   . 50             PUSH EAX
0040635A   . E8 310B0000    CALL SKINNER.00406E90   //这个CALL是关键,需要F8跟入,代码在后面分析。
0040635F   . 83C4 04        ADD ESP,4
00406362   . 8BF8           MOV EDI,EAX
00406364   . 8D4424 28      LEA EAX,DWORD PTR SS:[ESP+28] //前4位注册码地址送EAX。
00406368   . 50             PUSH EAX
00406369   . E8 A20B0000    CALL SKINNER.00406F10   //这个CALL是关键,需要F8跟入,代码在后面分析。   
0040636E   . 83C4 04        ADD ESP,4
00406371   . 85C0           TEST EAX,EAX
00406373   . 75 35          JNZ SHORT SKINNER.004063AA  //此处跳转
*省去无用代码
004063AA   > 3BC7           CMP EAX,EDI      //这里比较得到的用户名累加和和注册码累加和。不相等就OVER。所以注册码就是EAX内用户名累加和

,然后转换为10进制数即可得到注册码。
004063AC   . 75 34          JNZ SHORT SKINNER.004063E2
004063AE   . 8D4424 08      LEA EAX,DWORD PTR SS:[ESP+8]
004063B2   . 8B3D EC334100  MOV EDI,DWORD PTR DS:[<&KERNEL32.lstrcpy>
004063B8   . 50             PUSH EAX
004063B9   . 68 00294100    PUSH SKINNER.00412900
004063BE   . FFD7           CALL EDI
004063C0   . 8D4C24 28      LEA ECX,DWORD PTR SS:[ESP+28]
004063C4   . 51             PUSH ECX
004063C5   . 68 80264100    PUSH SKINNER.00412680
004063CA   . FFD7           CALL EDI
004063CC   . 6A 02          PUSH 2
004063CE   . 56             PUSH ESI
004063CF   . FF15 D0344100  CALL DWORD PTR DS:[<&USER32.EndDialog>]
======================================================
上面代码返回到下面代码处:
00406248   . 68 F80B4100    PUSH SKINNER.00410BF8
0040624D   . 8B3D C8344100  MOV EDI,DWORD PTR DS:[<&USER32.SetDlgItemTextA>]
00406253   . 68 FB030000    PUSH 3FB
00406258   . 56             PUSH ESI
00406259   . FFD7           CALL EDI
0040625B   . 68 00294100    PUSH SKINNER.00412900
00406260   . 68 F8030000    PUSH 3F8
00406265   . 56             PUSH ESI
00406266   . FFD7           CALL EDI
00406268   . 68 80264100    PUSH SKINNER.00412680
0040626D   . 68 F9030000    PUSH 3F9
00406272   . 56             PUSH ESI
00406273   . FFD7           CALL EDI
00406275   . C705 140F4100 >MOV DWORD PTR DS:[410F14],1
0040627F   . 6A 40          PUSH 40
00406281   . 68 F00B4100    PUSH SKINNER.00410BF0
00406286   . 68 C80B4100    PUSH SKINNER.00410BC8
0040628B   . 56             PUSH ESI
0040628C   . FF15 80344100  CALL DWORD PTR DS:[<&USER32.MessageBoxA>]    //成功注册对话框
======================================================
2.重要函数分析:首先看40635A的 CALL 00406E90 ,代码如下:ECX'表示本轮循环的第一次赋值的结果
00406EAA  |> 0FBE0E         /MOVSX ECX,BYTE PTR DS:[ESI]     //依次取姓名字符,符号扩展后送ECX
00406EAD  |. 8BD1           |MOV EDX,ECX       //字符也送EDX
00406EAF  |. 46             |INC ESI
00406EB0  |. 8D0CC9         |LEA ECX,DWORD PTR DS:[ECX+ECX*8] //ECX=9*ECX'
00406EB3  |. 8D0CCA         |LEA ECX,DWORD PTR DS:[EDX+ECX*8] //ECX=8*ECX+EDX=73*ECX'
00406EB6  |. 8D0CC9         |LEA ECX,DWORD PTR DS:[ECX+ECX*8] //ECX=9*ECX=9*73*ECX'
00406EB9  |. 03CA           |ADD ECX,EDX      //ECX=ECX+EDX=658*ECX'
00406EBB  |. 8D0C49         |LEA ECX,DWORD PTR DS:[ECX+ECX*2]  // ECX=3*ECX=3*658*ECX'=1974*ECX'
00406EBE  |. 83C1 19        |ADD ECX,19        //ECX=ECX+19=1974*ECX'+19h
00406EC1  |. 03C1           |ADD EAX,ECX      //累加到EAX内。
00406EC3  |. 803E 00        |CMP BYTE PTR DS:[ESI],0     
00406EC6  |.^75 E2          \JNZ SHORT SKINNER.00406EAA    //没有结束继续循环上述过程。
00406EC8  |> 8D0445 0B00000>LEA EAX,DWORD PTR DS:[EAX*2+B]   //EAX=EAX*2+Bh,累加和乘以2,加B。
00406ECF  |. 3D 82841E00    CMP EAX,1E8482       //与1E8482常量比较,
00406ED4  |. 72 1C          JB SHORT SKINNER.00406EF2   //小于则跳走
00406ED6  |. 3D 3E548900    CMP EAX,89543E       //与89543E常量比较
00406EDB  |. 76 2A          JBE SHORT SKINNER.00406F07 //小于等于则跳走
------------------------------------------------------------------------
00406EF2  |> 8D0445 0800000>/LEA EAX,DWORD PTR DS:[EAX*2+8]  //继续乘以2 加8。
00406EF9  |. 3D 82841E00    |CMP EAX,1E8482
00406EFE  |.^72 F2          \JB SHORT SKINNER.00406EF2      //小于这个常量则循环。
00406F00  |. 8D0445 0800000>LEA EAX,DWORD PTR DS:[EAX*2+8]  //大于则继续乘以2 加8。
00406F07  |> 5E             POP ESI
00406F08  \. C3             RETN
======================================================
3.再看第二个关键的CALL的内容:
00406F10  /$ 53             PUSH EBX
00406F11  |. 56             PUSH ESI
00406F12  |. 8B7424 0C      MOV ESI,DWORD PTR SS:[ESP+C]
00406F16  |. 57             PUSH EDI
00406F17  |. 56             PUSH ESI
00406F18  |. 33DB           XOR EBX,EBX
00406F1A  |. FF15 50334100  CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] //取注册码长度
00406F20  |. 8D78 FF        LEA EDI,DWORD PTR DS:[EAX-1] //注册码地址送EDI
00406F23  |. B9 01000000    MOV ECX,1
00406F28  |. 85C0           TEST EAX,EAX
00406F2A  |. 7E 15          JLE SHORT SKINNER.00406F41
00406F2C  |> 0FBE143E       /MOVSX EDX,BYTE PTR DS:[ESI+EDI] //从最高位取注册码送EDX,循环开始
00406F30  |. 83EA 30        |SUB EDX,30         //减去30H,变成16进制数。
00406F33  |. 4F             |DEC EDI
00406F34  |. 0FAFD1         |IMUL EDX,ECX   //与ECX相乘,ECX是十进制数位权,个,十,百,千等。
00406F37  |. 03DA           |ADD EBX,EDX       //累加到EBX内
00406F39  |. 8D0C89         |LEA ECX,DWORD PTR DS:[ECX+ECX*4]  //计算下一位的位权,ECX=5*ECX'
00406F3C  |. 03C9           |ADD ECX,ECX           //ECX=2*ECX=10*ECX',所以是按照10进制乘位权。
00406F3E  |. 48             |DEC EAX
00406F3F  |.^75 EB          \JNZ SHORT SKINNER.00406F2C   //如果EAX不为0,继续循环
00406F41  |> 8BC3           MOV EAX,EBX      //累加和送EAX返回主调函数。
00406F43  |. 5F             POP EDI
00406F44  |. 5E             POP ESI
00406F45  |. 5B             POP EBX
00406F46  \. C3             RETN
=======================================================
4.这个小程序的C语言注册机,已经调试通过:
/*keygen for skinner of winamp*/
#include "stdio.h"
main()
{
char name[20];
long c=0,s=0;
int l,i;
clrscr();
printf("This keygen is made by Mr.QduWg\n");
printf("please input your name,the lenth is less 20 letters without including space");
scanf("%s",name);
printf("%s\n",name);
l=strlen(name);
printf("%d\n",l);
for(i=0;i<l;i++)
{ printf("name[i]=%x",*(name+i));
 c=name[i]*9;
 c=8*c+name[i];
 c=9*c;
 c=c+name[i];
 c=3*c;
 c=c+25;
 s=s+c;
}
printf("code1=%ld\n",s);
s=2*s+0xB;
printf("the code is %ld\n",s);
if(s<0x1e8482)
{do
 s=s*2+8;
 while(s<0x1e8482); }
else if(s<=0x89543e)
printf("Your Regsiter  key is %ld\n",s);
printf("Thank you for use this keygen!\n");
getchar();
}
=======================================================
后记:经过2个小时的调试加写这篇破文,如Ku大侠说的那样,我们应该抽时间坐下来好好分析注册算法,只有这样才可以提高水平。这就是我响应其号召

的一篇注册码分析破文,算法实在简单,让大侠见笑了!要知道不积圭步,无以至千里!所以,从简单的开始比较容易积累信心和经验!一开始分析比较

复杂的程序算法,如果失败也是暂时的,但是让人感到郁闷!茶饭不思!

给出几个结果:
wanggang --------3324627
liuxin-----------2625731(731??听起来比较可怕吧)
ilovecracker-----5030363

感谢您花费宝贵时间浏览。

QduWg
qduwg@163.com

2006年1月14日晚完稿