首页
社区
课程
招聘
[旧帖] [原创]对某网络游戏封包加密的一点研究心得 0.00雪花
发表于: 2008-8-4 10:04 8386

[旧帖] [原创]对某网络游戏封包加密的一点研究心得 0.00雪花

2008-8-4 10:04
8386
在论坛潜水将近三年,自觉太不好意思,近日对某一网络游戏的封包加密方式进行了研究,取得了一点心得,与众位大大一同分享。
    首先通过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解题方法汇总!

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 107
活跃值: (1628)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
2
我靠 这么牛 把IDA翻译成C++
2008-8-4 10:38
0
雪    币: 6092
活跃值: (654)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
3

解释部分太少了  看起来很累
能解说下吗
就是代码旁边添加解释
顺便写下流程
这样大家都能懂
你也对过程更清楚了
分析太少了 跳跃很大啊
2008-8-4 10:47
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
只要你有心
花点心思进去一样可以的
2008-8-4 10:47
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
没办法啊
原来我也想贴一些od分析的截图上去
但是权限不够
毕竟第一次发帖我也想做的好点啊
还有代码部分,重要的地方我都在后面注释了
我只知道大致的加密解密的运行方式
至于具体的加解密的算法,我说了
由于函数太复杂了,我只是把整个函数翻译过来而已
我也没时间搞懂到底是怎么运算的
而且每个游戏的算法都不一样
我只是给出了一个加解密算法的思路
2008-8-4 11:01
0
游客
登录 | 注册 方可回帖
返回
//