【标 题】: S-Demo v 2.0 注册算法分析
【工 具】: Peid,Ollydbg v1.10
【保护类型】: 无壳,未使用密码学算法
【难 度】: 中
【作 者】: 隐者无疆[BCG]
【目 的】: 练习注册算法的分析和注册机的编写。
【软件简介】:
S-Demo是一个大多被破解者用来作破解动画过程的软件,它可以记录你的屏幕上的任何动作及鼠标的移动过程,同时使用了较高的压缩 率。当然压缩率可以选择,在正常的操作情况下,每分钟的生成的文件大小在200K左右(800x600x32bits)。
S-Demo生成的文件可用软件附带的S-Player播放,也可生成可执行文件,在MS-Windows 9X,NT & Windows2000系统上脱离S-Demo播放。
支持三种模式(全屏、拉伸、窗口)的播放,建议在全屏模式下查看动画,效果更佳。
支持动画介绍,可说明动画作者及其想提供的一些必要信息。
【详细过程】:
1.用Peid 侦测。显示“Microsoft Visual C++ 6.0”。
2.关键代码如下:
。。。省略部分代码。。。
0040804D |. >repne scas byte ptr es:[edi] ; edi指向key
0040804F |. >not ecx
00408051 |. >dec ecx
00408052 |. >mov esi,ecx ; ecx 为key的长度
00408054 |. >mov cl,byte ptr ss:[esp+14] ; cl为key的首字符
00408058 |. >mov dword ptr ss:[esp+10],esi ; ss:[esp+10]=key_len
0040805C |> >/cmp byte ptr ds:[eax+41D528],cl ; key 的首字符决定ebp的取值
00408062 |. >|je short un_crack.00408097
00408064 |. >|inc eax ; key的第0-15个字符必须为字符表中的一个
00408065 |. >|cmp eax,10
00408068 |.^ >\jl short un_crack.0040805C ; key 的首字符与字符表表的第eax个字符相同
0040806A |> >mov edi,un_crack.0041D568 ; ASCII "!@#@#SDFG^*&"
0040806F |. >or ecx,FFFFFFFF
00408072 |. >xor eax,eax
00408074 |. >repne scas byte ptr es:[edi]
00408076 |. >not ecx
00408078 |. >sub edi,ecx
0040807A |. >mov eax,ecx
0040807C |. >mov esi,edi
0040807E |. >mov edi,edx
00408080 |. >shr ecx,2
00408083 |. >rep movs dword ptr es:[edi],dword ptr ds:[esi]
00408085 |. >mov ecx,eax
00408087 |. >and ecx,3
0040808A |. >rep movs byte ptr es:[edi],byte ptr ds:[esi]
0040808C |. >pop edi
0040808D |. >pop esi
0040808E |. >pop ebp
0040808F |. >pop ebx
00408090 |. >add esp,0CC
00408096 |. >retn
00408097 |> >cmp eax,10
0040809A |.^ >jge short un_crack.0040806A
0040809C |. >mov ebx,dword ptr ss:[esp+E4] ; ebx=name+"80586443474"
004080A3 |. >mov ebp,0F ; ebp=0x0F
004080A8 |. >sub ebp,eax ; eax=key的首字符序号;ebp赋初值
004080AA |. >mov edi,ebx ; edi-->name+"80586443474"
004080AC |. >or ecx,FFFFFFFF
004080AF |. >xor eax,eax
004080B1 |. >repne scas byte ptr es:[edi]
004080B3 |. >not ecx
004080B5 |. >dec ecx ; ecx=Len(name)+0x0B
004080B6 |. >xor edx,edx
004080B8 |. >mov eax,ecx
004080BA |. >mov ecx,7
004080BF |. >div ecx ; eax=Len(name)+0x0B ecx=7
004080C1 |. >mov ecx,edx ; ecx为上述除法的余数
004080C3 |. >test ecx,ecx
004080C5 |. >jnz short un_crack.004080F2 ; 此处必须跳转,故余数应为零,name长度可为(7*n-11)
004080C7 |. >mov edi,un_crack.0041D520 ; ASCII "Guest"
004080CC |. >or ecx,FFFFFFFF
004080CF |. >xor eax,eax
004080D1 |. >repne scas byte ptr es:[edi]
004080D3 |. >not ecx
004080D5 |. >sub edi,ecx ; edi-->"Guest"
004080D7 |. >mov edx,ecx
004080D9 |. >mov esi,edi
004080DB |. >mov edi,ebx
004080DD |. >shr ecx,2
004080E0 |. >rep movs dword ptr es:[edi],dword ptr ds:[esi]
004080E2 |. >mov ecx,edx
004080E4 |. >and ecx,3
004080E7 |. >rep movs byte ptr es:[edi],byte ptr ds:[esi]
004080E9 |. >mov esi,dword ptr ss:[esp+10]
004080ED |. >mov ecx,5 ; ecx=5
004080F2 |> >mov eax,ebp ; eax<==ebp
004080F4 |. >cdq
004080F5 |. >idiv ecx
004080F7 |. >movsx eax,byte ptr ds:[edx+ebx] ; ebx指向"Guest"
004080FB |. >and eax,8000000F ; eax为?
00408100 |. >jns short un_crack.00408107
00408102 |. >dec eax
00408103 |. >or eax,FFFFFFF0
00408106 |. >inc eax
00408107 |> >mov cl,byte ptr ds:[eax+41D528] ; eax应为?
0040810D |. >mov al,byte ptr ss:[esp+15] ; al为key的第二位,由上述条件决定
00408111 |. >cmp cl,al
00408113 |. >je short un_crack.0040811F ; 必须跳转
00408115 |. >mov edi,un_crack.0041D55C ; ASCII "99#SDFG^*&"
0040811A |. >jmp un_crack.004081E4
0040811F |> >lea eax,dword ptr ds:[esi-3] ; eax=esi-3
00408122 |. >xor edi,edi
00408124 |. >cdq
00408125 |. >sub eax,edx
00408127 |. >sar eax,1 ; (key_len-edx-3)/2
00408129 |. >test eax,eax
0040812B |. >jle un_crack.004081C0
00408131 |. >lea esi,dword ptr ss:[esp+16] ; esi指向key的第三位
00408135 |> >/mov dl,byte ptr ds:[esi+1] ; dl=key的第?+1位
00408138 |. >|xor ecx,ecx
0040813A |> >|/cmp byte ptr ds:[ecx+41D528],dl ; dl=字符表的第??位, 是则跳转
00408140 |. >||je short un_crack.0040815E ; 必须在某一次循环中从此处跳出
00408142 |. >||inc ecx
00408143 |. >||cmp ecx,10
00408146 |.^ >|\jl short un_crack.0040813A
00408148 |> >|xor dl,dl
0040814A |> >|mov bl,byte ptr ds:[esi]
0040814C |. >|xor ecx,ecx
0040814E |> >|/cmp byte ptr ds:[ecx+41D528],bl
00408154 |. >||je short un_crack.0040817C ; key的第?位为字符表中的第#个字符?是则跳转
00408156 |. >||inc ecx
00408157 |. >||cmp ecx,10
0040815A |.^ >|\jl short un_crack.0040814E
0040815C |. >|jmp short <un_crack. 关键赋值>
0040815E |> >|cmp ecx,10
00408161 |.^ >|jge short un_crack.00408148 ; 不能跳转
00408163 |. >|sub ecx,ebp ; ebp的值在前面已经
00408165 |. >|add ecx,3E80
0040816B |. >|mov edx,ecx
0040816D |. >|and edx,8000000F
00408173 |.^ >|jns short un_crack.0040814A
00408175 |. >|dec edx
00408176 |. >|or edx,FFFFFFF0
00408179 |. >|inc edx
0040817A |.^ >|jmp short un_crack.0040814A
0040817C |> >|cmp ecx,10
0040817F |. >|jge short <un_crack. 关键赋值>
00408181 |. >|sub ecx,ebp ; ecx=?
00408183 |. >|add ecx,3E80
00408189 |. >|and ecx,8000000F
0040818F |. >|jns short un_crack.0040819A
00408191 |. >|dec ecx
00408192 |. >|or ecx,FFFFFFF0
00408195 |. >|inc ecx
00408196 |. >|jmp short un_crack.0040819A
00408198 >|> >|xor cl,cl
0040819A |> >|mov bl,cl
0040819C |. >|add esi,2
0040819F |. >|shl bl,4 ; bl=bl*0x10
004081A2 |. >|add bl,dl ; bl=?
004081A4 |. >|mov edx,dword ptr ss:[esp+E0]
004081AB |. >|movsx ecx,cl
004081AE |. >|mov byte ptr ds:[edi+edx],bl ; 关键赋值。生成字符串,为后面的比较作准备
004081B1 |. >|add ebp,ecx ; ebp的值在此处更新
004081B3 |. >|inc edi
004081B4 |. >|cmp edi,eax
004081B6 |.^ >\jl un_crack.00408135
004081BC |. >mov esi,dword ptr ss:[esp+10] ; esi=len(key)
004081C0 |> >lea edx,dword ptr ds:[esi+ebp-3] ; edx=ebp+len(key)-3
004081C4 |. >and edx,8000000F
004081CA |. >jns short un_crack.004081D1
004081CC |. >dec edx
004081CD |. >or edx,FFFFFFF0
004081D0 |. >inc edx
004081D1 |> >mov cl,byte ptr ss:[esp+esi+13] ;
004081D5 |. >mov bl,byte ptr ds:[edx+41D528] ;
004081DB |. >cmp cl,bl ; key 的最后一个字符字符表的第edx个字符
004081DD |. >je short un_crack.00408211 ; 此处必须跳转
004081DF |. >mov edi,un_crack.0041D550 ; ASCII "45#SDFG^*&"
004081E4 |> >or ecx,FFFFFFFF
004081E7 |. >xor eax,eax
004081E9 |. >repne scas byte ptr es:[edi]
004081EB |. >not ecx
004081ED |. >sub edi,ecx
004081EF |. >mov edx,ecx
004081F1 |. >mov esi,edi
004081F3 |. >mov edi,dword ptr ss:[esp+E0]
004081FA |. >shr ecx,2
004081FD |. >rep movs dword ptr es:[edi],dword ptr ds:[esi]
004081FF |. >mov ecx,edx
00408201 |. >and ecx,3
00408204 |. >rep movs byte ptr es:[edi],byte ptr ds:[esi]
00408206 |. >pop edi
00408207 |. >pop esi
00408208 |. >pop ebp
00408209 |. >pop ebx
0040820A |. >add esp,0CC
00408210 |. >retn
00408211 |> >mov ecx,dword ptr ss:[esp+E0]
00408218 |. >pop edi
00408219 |. >pop esi
0040821A |. >pop ebp
0040821B |. >mov byte ptr ds:[eax+ecx],0
0040821F |. >pop ebx
00408220 |. >add esp,0CC
00408226 \. >retn
返回到此处
004083C8 |. >add esp,18
004083CB |. >mov edi,un_crack.0041D1D0 ; ASCII "Clayman"
004083D0 |. >mov esi,ebx ; ebx = ds:[0012E0F8]
004083D2 |> >/mov dl,byte ptr ds:[esi]
004083D4 |. >|mov cl,byte ptr ds:[edi]
004083D6 |. >|mov al,dl
004083D8 |. >|cmp dl,cl
004083DA |. >|jnz short <un_crack.出错!!!>
004083DC |. >|test al,al
004083DE |. >|je short un_crack.004083F6
004083E0 |. >|mov cl,byte ptr ds:[esi+1]
004083E3 |. >|mov dl,byte ptr ds:[edi+1]
004083E6 |. >|mov al,cl
004083E8 |. >|cmp cl,dl
004083EA |. >|jnz short <un_crack.出错!!!>
004083EC |. >|add esi,2
004083EF |. >|add edi,2
004083F2 |. >|test al,al
004083F4 |.^ >\jnz short un_crack.004083D2
004083F6 |> >xor eax,eax
004083F8 |. >jmp short <un_crack.Yahoo!!!>
004083FA >|> >sbb eax,eax
004083FC |. >sbb eax,-1
004083FF >|> >test eax,eax
00408401 |. >je short <un_crack.成功!!>
。。。省略以后的代码。。。
3.注册码验证算法分析:
字符表:str1[]="FZRHK01WGTPQSAVC",固定存放在ds:[41d528]
str2[]="Guest"
str3[]="Clayman"
对注册名的要求:其长度name_len加11的和,为7的倍数
对注册码的要求:
注册码长度key_len为17
注册码的第一个字符必须为字符表str1中的一个,记其位置为n1;ebp赋初值为0x0F-n1。
注册码的第二个字符必须为str1[str2[ebp%5]&0xf]。
注册码的第三至第十六个字符满足如下条件:
为了便于叙述,设整型变量i. (i=0,1,2,3,4,5,6),注册码的第(2*i+3)位和第(2*i+4)位分别对应字符表str1中的第n2和第n3个字符,
bl=(((n1-ebp+0x3E80)&0x8000000F)<<4),dl=((n2-ebp+0x3E80)&0x8000000F).
(bl+dl)等于对应的str3[i]的Ascii码值。
另外,从第三位到第十六位每比较完两位,ebp的值进行一次更新。更新的方法是:ebp=ebp+al
注册码的第十七位必须为str1[(key_len+ebp-3)&0x8000000F].
4.注册码生成算法分析:
注册码生成算法的一个关键是其中的第三至第十六个字符的生成。
现在将str3中的字符的Ascii码值写成十六进制的形式,以'C'为例,它的Ascii码值为0x43。仔细分析可知,事实上bl和dl分别对应此Ascii码值的高4位和低4位。这些值是事先已经确定下来的。
接下来就要看,如何选择n1和n2使它们经过一系列运算后,得到正确的bl和dl。以n1的产生为例:
由dl=(n2-ebp+0x3E80)&0x8000000F可见,一个数与0x8000000F的and运算可以近似认为让它对0x10取模。
即dl=(n2-ebp+0x3E80)%0x10。
所以,n2=0x10*N+dl+ebp-0x3E80. 其中,dl为[0,15]之间的整数,ebp由前面的运算确定。通过尝试得到N的一个合适的取值,从而确定
n2的值。
5.注册机源代码
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
int seed=0;
char name[20];
char find_char(int ebp,int i)
{
const char str[]="FZRHK01WGTPQSAVC";
const con_int[]={0x4,0x3,0x6,0xC,0x6,0x1,0x7,0x9,0x6,0xD,0x6,0x1,0x6,0xE};
int n=0x3e0,flag=0,index;
while(!flag)
{
index=ebp+con_int[i]+n*0x10-0x3e80;
if(index>=0&&index<16)
flag=1;
else
++n;
}
return str[index];
}
int rand()
{
return 19841113*seed%0x10;
}
void main()
{
char key[50],ch;
const char str[]="FZRHK01WGTPQSAVC\0\0\0",str2[]="Guest";
unsigned int r,i,name_len,key_len=17,index=0;
const con_int[]={0x4,0x3,0x6,0xC,0x6,0x1,0x7,0x9,0x6,0xD,0x6,0x1,0x6,0xE};
unsigned long ebp,edx,ecx;
printf("********************************************************************************\n");
printf(" Keygen for S-Demo v 2.0 \n");
printf(" maker:隐者无疆[BCG] \n");
printf("********************************************************************************\n");
printf("请输入注册名(其长度加上11后,应为7的倍数):");
scanf("%s",&name);
name_len=strlen(name);
while((name_len+11)%7!=0)
{
printf("请输入注册名(其长度加上11后,应为7的倍数):");
scanf("%s",&name);
name_len=strlen(name);
}
printf("请输入一个整数,用作生成随机数的种子:");
scanf("%d",&seed);
r=rand();
ebp=15-r;
edx=ebp%5;
ecx=ebp/5;
key[index++]=str[r]; //通过生成伪随机数,确定注册码的第一位。
key[index++]=str[str2[edx]&0xf]; //生成注册码的第二位
if(ecx&0x8000==1) //模拟cdq
edx=0xffff;
else
edx=0;
for(i=0;i<(key_len-edx-3)/2;i++) //生成注册码的第三至十六位
{
ch=find_char(ebp,2*i);
key[index++]=ch;
ch=find_char(ebp,2*i+1);
key[index++]=ch;
ebp+=con_int[2*i]; //更新ebp的值
}
key[index++]=str[(key_len+ebp-3)%0x10]; //生成注册码的第十七位
key[index]='\0';
printf("你的注册码为%s\n",&key);
printf("Press any key to abort.");
getchar();
getchar();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课