在论坛潜水将近三年,自觉太不好意思,近日对某一网络游戏的封包加密方式进行了研究,取得了一点心得,与众位大大一同分享。
首先通过wpe获取封包,如下:
1 1 Recv
0000 01 .
2 1 Recv
0000 08 .
3 8 Recv
0000 8C 7E CC 28 D9 60 7D FD .~.(.`}.
4 42 Send
0000 2A 00 57 EB FB B2 94 58 95 BD A6 C2 7C 4D 62 EC *.W....X....|Mb.
0010 B7 E8 D1 85 9B A4 29 5A 7D 4F B7 2C D1 6E BD 29 ......)Z}O.,.n.)
0020 C9 CB 3E 94 23 C9 1B 85 4A D7 ..>.#...J.
反复截取好几次封包,发现第三次接收的8字节数据会变动,第四个发送的数据包也会变动。猜想可能是把第三个8字节的数据作为密钥。
通过OD跟踪,发现第四个发送的数据包的原型是:
2A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 *.............. 2A是数据包的长度
09 01 00 00 5B 02 00 00 00 00 00 00
31 2E 30 30 ...[......1.00
2E 30 35 00 0D F0 AD BA EE FE .05..瓠侯 1.00.05是版本号
所以分析思路就变成如何利用接受的8字节数据去加密发送的42字节的数据。
利用od跟踪,对接收的8个字节数据的内存下断,找到断点。
用IDA分析文件,找到断点,发现断点在fun_3840函数中,这是一个比较复杂的函数,很难分析清楚具体的算法。没办法,只能使用土办法,把整个汇编函数翻译成C语言。翻译结果如下:
#include "StdAfx.h"
#include "JieMi.h"
extern DWORD dword_9520[18];
extern DWORD dword_9568[1024];
extern char *dword_9480;
extern char *dword_AA38;
extern char *dword_AA34;
extern char *dword_AA30; //这几个都是已经定义全局变量,可以直接在ida文件中找到。
JieMi::JieMi(void) //i1048、i104C、i1050、i1054、dd[0]到dd[1041]都是类的成员变量
{
this->i1048=3; //在接受数据之前就已经赋值,因此我使用构造函数进行赋值。
this->i104C=2;
this->i1050=1;
this->i1054=0;
}
JieMi::~JieMi(void)
{
}
int JieMi::fun_3840(BYTE * lpBuffer,int BufferLen) // lpBuffer接受的8字节数据地址BufferLen是数据长度
{
if (BufferLen>56)
return -1;
this->fun_2CB0(); //利用fun_2CB0对dd[0]到dd[1041]进行初始化
int ecx=0;
int var_4=3;
int ebx=BufferLen;
int i=2;
do //对接收的8字节数据进行变换
{
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i-2]=this->dd[i-2]^(DWORD)BufferLen;
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i-1]=this->dd[i-1]^(DWORD)BufferLen;
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i]=this->dd[i]^(DWORD)BufferLen;
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i+1]=this->dd[i+1]^(DWORD)BufferLen;
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i+2]=this->dd[i+2]^(DWORD)BufferLen;
i=i+6;
*((char*)&BufferLen+this->i1048)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i104C)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1050)=lpBuffer[ecx%ebx];
ecx++;
*((char*)&BufferLen+this->i1054)=lpBuffer[ecx%ebx];
ecx++;
this->dd[i-3]=this->dd[i-3]^(DWORD)BufferLen;
var_4--;
}while(var_4!=0);
DWORD BuL=0,Bup=0;
i=0;
do //对dd[0]到dd[17]进行变换
{
this->fun_2D60(&BuL,&Bup);
this->dd[i]=BuL;
this->dd[i+1]=Bup;
i=i+2;
}while(i<18);
i=4;
int d=19;
do //对dd[18]到dd[1041]进行变换
{
int j=128;
do
{
this->fun_2D60(&BuL,&Bup);
this->dd[d-1]=BuL;
this->dd[d]=Bup;
d=d+2;
j--;
}while(j!=0);
i--;
}while(i!=0);
return 0;
}
int JieMi::fun_2CB0() //对dd[0]到dd[1041]赋初值
{
int i=0,j=18,k=0;
do
{
this->dd[i]=dword_9520[i];
i++;
j--;
}while(j!=0);
i=18,j=1024,k=0;
do
{
this->dd[i]=dword_9568[k];
i++;
j--;
k++;
}while(j!=0);
return 0;
}
void JieMi::fun_2D60(DWORD *arg_0,DWORD *arg_4) //对传进来的两个DWORD数进行变换
{
arg_0[0]=arg_0[0]^this->dd[0];
BYTE* pax=(BYTE*)arg_0;
DWORD dx=pax[this->i1048];
DWORD si=pax[this->i104C];
DWORD di=this->dd[dx+0x48/4];
di+=this->dd[si+0x448/4];
dx=pax[this->i1050];
DWORD bx=this->dd[dx+0x848/4];
dx=pax[this->i1054];
si=this->dd[dx+0xc48/4];
di=di^bx;
di+=si;
bx=this->dd[1];
si=arg_4[0];
di=di^bx;
si=si^di;
arg_4[0]=si;
BYTE* pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
si=this->dd[si+0x48/4];
di=pdx[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pdx[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_0[0];
si+=bx;
si=si^this->dd[2];
di=di^si;
arg_0[0]=di;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si=si+bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[3];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
di=pdx[this->i1050];
si=this->dd[si+0x48/4];
si+=bx;
bx=this->dd[di+0x848/4];
si=si^bx;
di=pdx[this->i1054];
si+=this->dd[di+0xc48/4];
si=si^this->dd[0x10/4];
arg_0[0]=arg_0[0]^si;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[0x14/4];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
si=this->dd[si+0x48/4];
di=pdx[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pdx[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_0[0];
si+=bx;
si=si^this->dd[0x18/4];
di=di^si;
arg_0[0]=di;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si=si+bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[0x1c/4];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
di=pdx[this->i1050];
si=this->dd[si+0x48/4];
si+=bx;
bx=this->dd[di+0x848/4];
si=si^bx;
di=pdx[this->i1054];
si+=this->dd[di+0xc48/4];
si=si^this->dd[0x20/4];
arg_0[0]=arg_0[0]^si;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[0x24/4];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
si=this->dd[si+0x48/4];
di=pdx[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pdx[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_0[0];
si+=bx;
si=si^this->dd[0x28/4];
di=di^si;
arg_0[0]=di;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si=si+bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[0x2c/4];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
di=pdx[this->i1050];
si=this->dd[si+0x48/4];
si+=bx;
bx=this->dd[di+0x848/4];
si=si^bx;
di=pdx[this->i1054];
si+=this->dd[di+0xc48/4];
si=si^this->dd[0x30/4];
arg_0[0]=arg_0[0]^si;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
si+=this->dd[di+0xc48/4];
bx=this->dd[0x34/4];
di=arg_4[0];
si=si^bx;
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
si=this->dd[si+0x48/4];
di=pdx[this->i1050];
si+=bx;
bx=this->dd[di+0x848/4];
di=pdx[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_0[0];
si+=bx;
si=si^this->dd[0x38/4];
di=di^si;
arg_0[0]=di;
pax=(BYTE*)arg_0;
di=pax[this->i104C];
bx=this->dd[di+0x448/4];
si=pax[this->i1048];
si=this->dd[si+0x48/4];
di=pax[this->i1050];
si=si+bx;
bx=this->dd[di+0x848/4];
di=pax[this->i1054];
si=si^bx;
bx=this->dd[di+0xc48/4];
di=arg_4[0];
si+=bx;
si=si^this->dd[0x3c/4];
di=di^si;
arg_4[0]=di;
pdx=(BYTE*)arg_4;
di=pdx[this->i104C];
bx=this->dd[di+0x448/4];
si=pdx[this->i1048];
di=pdx[this->i1050];
si=this->dd[si+0x48/4];
si+=bx;
bx=this->dd[di+0x848/4];
si=si^bx;
di=pdx[this->i1054];
si+=this->dd[di+0xc48/4];
si=si^this->dd[0x40/4];
arg_0[0]=arg_0[0]^si;
DWORD cx=this->dd[0x44/4];
si=arg_4[0];
si=si^cx;
arg_4[0]=si;
cx=si;
si=arg_0[0];
arg_0[0]=cx;
arg_4[0]=si;
}
分析接受数据的结果如下:
程序利用接收的8字节数据,对dd[0]到dd[1041]进行赋值并变换。
接下去分析程序如何对42个字节的发送数据进行加密:
首先利用od下recv断点,然后一直往前找,找到加密的代码段,用IDA进行分析,把汇编语言翻译成c语言,代码如下:
int JieMi::fun_2550(BYTE *lpBuff,int BufferLen)
//lpBuff是指向待加密数据的第三个字节的地址,因为最前面的2A 00是不变的,是数据包的长度。
{
return this->fun_3D60(lpBuff,BufferLen,(BYTE *) dword_9480,0);
}
int JieMi::fun_3D60(BYTE *lpBuff,int BufferLen,BYTE *arg_8,DWORD * arg_C)
{
DWORD cx,var_C,var_8;
BYTE *var_14;
BYTE *var_10, *bx;
var_14=lpBuff;
if (lpBuff==0)
return -3;
if (BufferLen%8 != 0)
return -1;
if (arg_8==0)
return -2;
if (arg_C==0)
{
DWORD var_C=0;
cx=0;
}
else
{
var_C=arg_C[0];
cx=arg_C[1];
}
BYTE *ax=(BYTE *)&var_C;
var_8=cx;
var_10=lpBuff;
bx= lpBuff+BufferLen;
int j=0;
for(int i=0;i<4;i++)
{
if(dword_9480[i]!=arg_8[i])
j=1;
}
if(j==0)
{
if(var_14<bx)
{
do //这里就是进行加密的代码 8个字节8个字节的加密
{
this->fun_3B00(var_10);
var_10+=8;
}while(var_10<bx);
}
return 0;
}
j=0;
for(int i=0;i<4;i++)
{
if(dword_AA38[i]!=arg_8[i])
j=1;
}
if(j==0)
{
if(var_14>=bx)
return 0;
int i=(int)(&bx[0]-&var_14[0]);
i--;
i=i>>3;
BYTE * si=var_14;
i++;
do
{
si[0]=si[0]^ax[0];
si[1]=si[1]^ax[1];
si[2]=si[2]^ax[2];
si[3]=si[3]^ax[3];
si[4]=si[4]^ax[4];
si[5]=si[5]^ax[5];
si[6]=si[6]^ax[6];
si[7]=si[7]^ax[7];
this->fun_3B00(si);
si+=8;
i--;
}while(i!=0);
return 0;
}
j=0;
for(int i=0;i<4;i++)
{
if(dword_AA34[i]!=arg_8[i])
j=1;
}
if(j==0)
{
if(var_14>=bx)
return 0;
int i=(int)(&bx[0]-&var_14[0]);
i--;
i=i>>3;
BYTE * si=var_14;
i++;
do
{
BYTE *cx1=(BYTE *)&var_C;
this->fun_3B00(cx1);
BYTE *ax1=(BYTE *)&var_C;
si[0]=si[0]^ax1[0];
si[2]=si[2]^ax1[2];
si[1]=si[1]^ax1[1];
si[3]=si[3]^ax1[3];
BYTE *ax2=(BYTE *)&var_8;
si[4]=si[4]^ax2[0];
si[5]=si[5]^ax2[1];
si[6]=si[6]^ax2[2];
si[7]=si[7]^ax2[3];
var_C=*((DWORD*)&si[0]);
var_8=*((DWORD*)&si[4]);
si+=8;
bx--;
}while(bx!=0);
return 0;
}
j=0;
for(int i=0;i<4;i++)
{
if(dword_AA30[i]!=arg_8[i])
j=1;
}
if(j==1)
return -3;
if(var_14>=bx)
return 0;
int i=(int)(&bx[0]-&var_14[0]);
i--;
i=i>>3;
BYTE * si=var_14;
i++;
do
{
this->fun_3B00((BYTE *)&var_C);
BYTE *ax1=(BYTE *)&var_C;
si[0]=si[0]^ax1[0];
si[2]=si[2]^ax1[2];
si[1]=si[1]^ax1[1];
si[3]=si[3]^ax1[3];
BYTE *ax2=(BYTE *)&var_8;
si[4]=si[4]^ax2[0];
si[5]=si[5]^ax2[1];
si[6]=si[6]^ax2[2];
si[7]=si[7]^ax2[3];
si+=8;
bx--;
}while(bx!=0);
return 0;
}
void JieMi::fun_3B00(BYTE *arg_0)
{
BYTE temp[4],var_4[4];
temp[0]=arg_0[0];
temp[1]=arg_0[1];
temp[2]=arg_0[2];
temp[3]=arg_0[3];
var_4[0]=temp[this->i1048];
var_4[1]=temp[this->i104C];
var_4[2]=temp[this->i1050];
var_4[3]=temp[this->i1054];
arg_0[0]=var_4[0];
arg_0[1]=var_4[1];
arg_0[2]=var_4[2];
arg_0[3]=var_4[3];
temp[0]=arg_0[4];
temp[1]=arg_0[5];
temp[2]=arg_0[6];
temp[3]=arg_0[7];
var_4[0]=temp[this->i1048];
var_4[1]=temp[this->i104C];
var_4[2]=temp[this->i1050];
var_4[3]=temp[this->i1054];
arg_0[4]=var_4[0];
arg_0[5]=var_4[1];
arg_0[6]=var_4[2];
arg_0[7]=var_4[3];
this->fun_2D60((DWORD *)&arg_0[0],(DWORD *)&arg_0[4]);
temp[this->i1048]=arg_0[0];
temp[this->i104C]=arg_0[1];
temp[this->i1050]=arg_0[2];
temp[this->i1054]=arg_0[3];
arg_0[0]=temp[0];
arg_0[1]=temp[1];
arg_0[2]=temp[2];
arg_0[3]=temp[3];
temp[this->i1048]=arg_0[4];
temp[this->i104C]=arg_0[5];
temp[this->i1050]=arg_0[6];
temp[this->i1054]=arg_0[7];
arg_0[4]=temp[0];
arg_0[5]=temp[1];
arg_0[6]=temp[2];
arg_0[7]=temp[3];
}
总结:程序首先利用接收的8个字节数据,来加密类的成员变量dd[0]到dd[1041],再利用dd[0]到dd[1041]变量来加密发送的数据。
本来想用图片来详细说明的,但是权限不够,不能上传附件。:(
由于不能上传附件,有需要exe文件进行测试的给我发email:jijian324@163.com
有志同道合或想进行技术交流的加我QQ:5997284。
http://www.live-share.com/files/341528/CeShi.rar.html
这个是打包的exe文件,只要依次输入8个接收的16进制数,就能够显示出待发送的42字节数据。
大家可以试一下。
游戏是蜀山Online,版本1.00.05(当前版本)。
[课程]Android-CTF解题方法汇总!