CM介绍:
这是一个www.crackmes.de上2006的CM。
Download Jade.zip, 6 kb
Browse contents of Jade.zip
This is some thing different from others....
No crypto....But some thing else....
Difficulty: 2 - Needs a little brain (or luck)
Platform: Windows 2000/XP only
Language: C/C++
Published: 26. Jul, 2006
Downloads: 696
在看雪的Crackme2007全集里也有,但不详细!
一、打开这个CM,发现没得文本控件,不能输入用户名和密码之类的。只有两个按键,一个“注册”和“关闭”。点注册也没有反应。用OD打开,查找字串,找到:
00401BE0 . A1 5C464000 mov eax,dword ptr ds:[0x40465C]
00401BE5 . 56 push esi
00401BE6 . 85C0 test eax,eax
00401BE8 . 8BF1 mov esi,ecx
00401BEA . 74 22 je X00401C0E
00401BEC . 68 58414000 push 00404158 ; Registered!!!
00401BF1 . 68 E8030000 push 0x3E8
00401BF6 . E8 59020000 call <jmp.&MFC42.#CWnd::SetDlgItemTextA_>
看看流程,[0x40465C]里为非0才注册成功。[0x40465C]在哪里赋值呢?用OD的查找常量0x40465C,找到:
00401795 |. 51 push ecx ; 组1
00401796 |. 52 push edx ; data+t1
00401797 |. E8 44FBFFFF call 004012E0
0040179C |. 83C4 08 add esp,0x8
0040179F |. A3 5C464000 mov dword ptr ds:[0x40465C],eax
只有一个地方,就是call 004012E0的返回值。向上看看,看到:
004016D7 |. E8 8E060000 |call <jmp.&MFC42.#CAsyncSocket::Receive_5478> ; }
居然用CAsyncSocket的Receive来接收数据,看来要先补一补CAsyncSocket(我有另一个文章专门介绍CAsyncSocket的)。
二、根据CAsyncSocket编程流程,先要找到它的Create方法,在OD反汇编窗口,按Ctrl+N,找到:MFC42.#CAsyncSocket::Create_2077,点它,按回车,OK。
0040141E |. 56 push esi
0040141F |. 6A 3F push 0x3F
00401421 |. 6A 01 push 0x1
00401423 |. 68 31200000 push 0x2031
00401428 |. B9 48424000 mov ecx,00404248
0040142D |. 8975 F8 mov [local.2],esi
00401430 |. 8975 F4 mov [local.3],esi
00401433 |. E8 4A090000 call <jmp.&MFC42.#CAsyncSocket::Create_2077>
看看Create的参数,Create(0x2031,0x1,0x3F,0),它监听着本机地址的上0x2031端口,所以客户端也要这样。进入监听后,它循环执行Accept(并非CAsyncSocket的正确方法)
,直到有客户端连接。
三、分析Accept后的代码,由于代码太多,这里不粘出来了,主要是4014D6到40179F。以下是分析:
1、4014D6,发送一个“hello”给客户湍。
2、401503,循环接收数据,大小为0x64,记为KEY1。
3、401514,是一个比较函数,比较刚才接收的是否是"---...:: [CYc] ::...---"。
4、401524,随机返回一个数值,再转换成16进制形式的字串,记为RAND。
5、401547,一个简单的反Debug。
6、40158F,循环接收数据,大小为0x64,记为DATA。
7、4015B2,发RAND。
8、4015EC,循环接收数据,大小为0x64,记为SN。
9、401620,循环接收数据,大小为0x64,分析发现这个数据没有用。
10、40162A,是一个反调试,用带反反调试的OD不用理。
11、40168B,循环接收数据,大小为0x64,记为T1。
12、40169D,把T1看成一个8进制字串,转换成16进制,和RAND比较,不对就OVER。
13、4016D7,循环接收数据,大小为0x64,记为KEY2。
14、4016E8,是一个比较函数,比较刚才接收的是否是"---...:: [cYC] ::...---"。
15、401713,把SN转换成SN1+‘-’+SN2。
16、40173F,这段前后是复制字串生成SN1+DATA+T1和DATA+T1。
17、40177A,两个作用。1.生成一个固定的整数数组,记为K1。2.根据K1和SN1+DATA+T1运算出一个数与SN2比较,不对就OVER。
18、401797,根据DATA+T1运算出两个数,转换成16进制字串后与SN1比较,不对就OVER。
19、[0x40465C]为1后,再点注册就可以注册成功了。
四、写逆向代码
先写RAND到T1的算法:
sscanf(RAND,"%x",&len);
sprintf(T1,"%o",len);
SN1的算法,我是直接抠汇编:
void CMySocket::GetSN1(const char *indata, char *&outdata)
{
outdata=new char[17];
DWORD a=0,b=0;
__asm
{
pushad
xor eax,eax
xor edx,edx
mov edi,indata
xor esi,esi
mov cl,byte ptr ds:[edi]
test cl,cl
je exit1
start1:
movsx ecx,cl
xor ecx,0x159753
mov eax,ecx
add edx,ecx
or eax,0x237891
not eax
and eax,ecx
mov cl,byte ptr ds:[esi+edi+0x1]
add eax,edx
lea edx,dword ptr ds:[edx+esi-0x1]
xor edx,0xA6542689
inc esi
test cl,cl
jnz start1
exit1:
mov a,edx
mov b,eax
popad
}
sprintf(outdata,"%08X%08x",a,b);
}
SN2的算法:
void CMySocket::GetSN2(const char *indata, char *&outdata)
{
outdata=new char[9];
DWORD sn=0xFFFFFFFF,k=0,*p=0;
GetK1(p);
__asm
{
pushad
mov edi,p
mov edx,indata
or eax,0xFFFFFFFF
movsx ecx,byte ptr ds:[edx]
test ecx,ecx
je exit1
inc edx
loop1:
xor ecx,eax
and ecx,0xFF
shr eax,0x8
mov ecx,dword ptr ds:[ecx*4+edi]
xor eax,ecx
movsx ecx,byte ptr ds:[edx]
inc edx
test ecx,ecx
jnz loop1 ; 运算取EAX,要逆这个
exit1:
not eax
mov sn,eax
popad
}
sprintf(outdata,"%x",sn);
delete p;
}
K1的算法:
int CMySocket::GetK1(DWORD *&K1)
{
K1=new DWORD[0x400];
DWORD t=0,i=0,j=0;
for (i=0;i<0x400;i++)
{
t=i;
for (j=0;j<8;j++)
{
if (t&1==1)
{
__asm
{
pushad
mov eax,t
shr eax,1
xor eax,0xEDB88320
mov t,eax
popad
}
}
else
{
__asm
{
pushad
mov eax,t
shr eax,1
mov t,eax
popad
}
}
}
K1[i]=t;
}
return 0;
}
五、写客户端
void CJade_CMDlg::On1()
{
const char key1[]="---...:: [CYc] ::...---";
const char key2[]="---...:: [cYC] ::...---";
char buffer[0x65]={0},*data=0,*sn1=0,*sn2=0,*sn=0,*temp=0;
char t1[11]={0};
DWORD len=0,stop=0,errorcode=0;
if( AfxSocketInit() == FALSE)
{
AfxMessageBox("Failed to Initialize Sockets");
return;
}
CAsyncSocket s;
if(s.Create(0,SOCK_STREAM,0x3f,"127.0.0.1")==FALSE)
{
myerror(GetLastError());
MessageBox("Failed to Create Socket");
return;
}
s.Connect("127.0.0.1",8241);
Sleep(200);
stop=0;
errorcode=s.Receive(buffer,5);
while(-1==errorcode)
{
errorcode=s.Receive(buffer,5);
stop++;
if (stop>10000)
{
AfxMessageBox("Receive1 out time!");
return;
}
}
s.Send(key1,strlen(key1));
memset(buffer,0xee,0x64);
s.Send(buffer,0x64);
stop=0;
errorcode=s.Receive(t1,2);
while(-1==errorcode)
{
errorcode=s.Receive(t1,2);
stop++;
if (stop>10000)
{
AfxMessageBox("Receive2 out time!");
return;
}
}
sscanf(t1,"%x",&len);
sprintf(t1,"%o",len);
len=0x64+strlen(t1);
data=new char[len+1];
ZeroMemory(data,len);
memset(data,0xee,0x64);
strcat(data,t1);
GetSN1(data,sn1);
delete [] data;
len+=strlen(sn1);
data=new char[len+1];
ZeroMemory(data,len);
strcpy(data,sn1);
temp=(char*)((DWORD)data+strlen(sn1));
memset(temp,0xee,0x64);
strcat(data,t1);
GetSN2(data,sn2);
delete [] data;
len=strlen(sn1)+strlen(sn2);
sn=new char[len+2];
strcpy(sn,sn1);
strcat(sn,"-");
strcat(sn,sn2);
s.Send(sn,strlen(sn));
s.Send(sn,strlen(sn));
s.Send(t1,strlen(t1));
s.Send(key2,strlen(key2));
s.Close();//*/
delete [] sn,sn1,sn2,data;
AfxMessageBox("可以注册了!");
}
总结:要注意的是客户端的IP,对本机而言,IP应该是“127.0.0.1”或是网的IP。端口要在连接时输入,在新建时输入会提示错误。