原地址
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打开,查找字串,找到:
[plain] view plaincopy
01.00401BE0 . A1 5C464000 mov eax,dword ptr ds:[0x40465C]
02.00401BE5 . 56 push esi
03.00401BE6 . 85C0 test eax,eax
04.00401BE8 . 8BF1 mov esi,ecx
05.00401BEA . 74 22 je X00401C0E
06.00401BEC . 68 58414000 push 00404158 ; Registered!!!
07.00401BF1 . 68 E8030000 push 0x3E8
08.00401BF6 . E8 59020000 call <jmp.&MFC42.#CWnd::SetDlgItemTextA_>
看看流程,[0x40465C]里为非0才注册成功。[0x40465C]在哪里赋值呢?用OD的查找常量0x40465C,找到:
[plain] view plaincopy
01.00401795 |. 51 push ecx ; 组1
02.00401796 |. 52 push edx ; data+t1
03.00401797 |. E8 44FBFFFF call 004012E0
04.0040179C |. 83C4 08 add esp,0x8
05.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。
[plain] view plaincopy
01.0040141E |. 56 push esi
02.0040141F |. 6A 3F push 0x3F
03.00401421 |. 6A 01 push 0x1
04.00401423 |. 68 31200000 push 0x2031
05.00401428 |. B9 48424000 mov ecx,00404248
06.0040142D |. 8975 F8 mov [local.2],esi
07.00401430 |. 8975 F4 mov [local.3],esi
08.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的算法:
[html] view plaincopy
01.sscanf(RAND,"%x",&len);
02.sprintf(T1,"%o",len);
SN1的算法,我是直接抠汇编:
[cpp] view plaincopy
01.void CMySocket::GetSN1(const char *indata, char *&outdata)
02.{
03. outdata=new char[17];
04. DWORD a=0,b=0;
05. __asm
06. {
07. pushad
08. xor eax,eax
09. xor edx,edx
10. mov edi,indata
11. xor esi,esi
12. mov cl,byte ptr ds:[edi]
13. test cl,cl
14. je exit1
15.
16.start1:
17. movsx ecx,cl
18. xor ecx,0x159753
19. mov eax,ecx
20. add edx,ecx
21. or eax,0x237891
22. not eax
23. and eax,ecx
24. mov cl,byte ptr ds:[esi+edi+0x1]
25. add eax,edx
26. lea edx,dword ptr ds:[edx+esi-0x1]
27. xor edx,0xA6542689
28. inc esi
29. test cl,cl
30. jnz start1
31.exit1:
32. mov a,edx
33. mov b,eax
34. popad
35. }
36. sprintf(outdata,"%08X%08x",a,b);
37.}
SN2的算法:
[cpp] view plaincopy
01.void CMySocket::GetSN2(const char *indata, char *&outdata)
02.{
03. outdata=new char[9];
04. DWORD sn=0xFFFFFFFF,k=0,*p=0;
05. GetK1(p);
06. __asm
07. {
08. pushad
09. mov edi,p
10. mov edx,indata
11. or eax,0xFFFFFFFF
12. movsx ecx,byte ptr ds:[edx]
13. test ecx,ecx
14. je exit1
15. inc edx
16.loop1:
17. xor ecx,eax
18. and ecx,0xFF
19. shr eax,0x8
20. mov ecx,dword ptr ds:[ecx*4+edi]
21. xor eax,ecx
22. movsx ecx,byte ptr ds:[edx]
23. inc edx
24. test ecx,ecx
25. jnz loop1 ; 运算取EAX,要逆这个
26.exit1:
27. not eax
28. mov sn,eax
29. popad
30. }
31. sprintf(outdata,"%x",sn);
32. delete p;
33.}
K1的算法:
[cpp] view plaincopy
01.int CMySocket::GetK1(DWORD *&K1)
02.{
03. K1=new DWORD[0x400];
04. DWORD t=0,i=0,j=0;
05. for (i=0;i<0x400;i++)
06. {
07. t=i;
08. for (j=0;j<8;j++)
09. {
10. if (t&1==1)
11. {
12. __asm
13. {
14. pushad
15. mov eax,t
16. shr eax,1
17. xor eax,0xEDB88320
18. mov t,eax
19. popad
20. }
21. }
22. else
23. {
24. __asm
25. {
26. pushad
27. mov eax,t
28. shr eax,1
29. mov t,eax
30. popad
31. }
32. }
33. }
34. K1[i]=t;
35. }
36. return 0;
37.}
五、写客户端
[cpp] view plaincopy
01.void CJade_CMDlg::On1()
02.{
03. const char key1[]="---...:: [CYc] ::...---";
04. const char key2[]="---...:: [cYC] ::...---";
05. char buffer[0x65]={0},*data=0,*sn1=0,*sn2=0,*sn=0,*temp=0;
06. char t1[11]={0};
07. DWORD len=0,stop=0,errorcode=0;
08.
09. if( AfxSocketInit() == FALSE)
10. {
11. AfxMessageBox("Failed to Initialize Sockets");
12. return;
13. }
14. CAsyncSocket s;
15. if(s.Create(0,SOCK_STREAM,0x3f,"127.0.0.1")==FALSE)
16. {
17. myerror(GetLastError());
18. MessageBox("Failed to Create Socket");
19. return;
20. }
21. s.Connect("127.0.0.1",8241);
22. Sleep(200);
23.
24. stop=0;
25. errorcode=s.Receive(buffer,5);
26. while(-1==errorcode)
27. {
28. errorcode=s.Receive(buffer,5);
29. stop++;
30. if (stop>10000)
31. {
32. AfxMessageBox("Receive1 out time!");
33. return;
34. }
35. }
36. s.Send(key1,strlen(key1));
37.
38. memset(buffer,0xee,0x64);
39. s.Send(buffer,0x64);
40.
41. stop=0;
42. errorcode=s.Receive(t1,2);
43. while(-1==errorcode)
44. {
45. errorcode=s.Receive(t1,2);
46. stop++;
47. if (stop>10000)
48. {
49. AfxMessageBox("Receive2 out time!");
50. return;
51. }
52. }
53. sscanf(t1,"%x",&len);
54. sprintf(t1,"%o",len);
55.
56. len=0x64+strlen(t1);
57. data=new char[len+1];
58. ZeroMemory(data,len);
59. memset(data,0xee,0x64);
60. strcat(data,t1);
61. GetSN1(data,sn1);
62. delete [] data;
63. len+=strlen(sn1);
64. data=new char[len+1];
65. ZeroMemory(data,len);
66. strcpy(data,sn1);
67. temp=(char*)((DWORD)data+strlen(sn1));
68. memset(temp,0xee,0x64);
69. strcat(data,t1);
70. GetSN2(data,sn2);
71. delete [] data;
72. len=strlen(sn1)+strlen(sn2);
73. sn=new char[len+2];
74. strcpy(sn,sn1);
75. strcat(sn,"-");
76. strcat(sn,sn2);
77.
78. s.Send(sn,strlen(sn));
79. s.Send(sn,strlen(sn));
80. s.Send(t1,strlen(t1));
81. s.Send(key2,strlen(key2));
82. s.Close();//*/
83. delete [] sn,sn1,sn2,data;
84. AfxMessageBox("可以注册了!");
85.}
总结:要注意的是客户端的IP,对本机而言,IP应该是“127.0.0.1”或是网的IP。端口要在连接时输入,在新建时输入会提示错误。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)