网络游戏协议封包需注意点
发表于2016-09-01
                        
                      对于强客户端的游戏,就有很大可能出现脱机挂,因为一切客户端的计算、处理逻辑都不需要进行,只需要将最理想的作弊数据包直接发给服务器,就能达到游戏收益最大化。
下面这几点是需要多加注意的点。
1、send函数
根据send调用的buffer,进行回溯,找到加密函数。对于send调用的buffer,最好是保持随机调用,如果固定的话会非常容易分析。
2、耦合、离散
软件工程中提倡低耦合,高内聚。但是就耦合这点在封包发送阶段,应该做到高耦合,这样可以让逆向分析陷入迷茫,找不到关键点。对于包结构或者组包过程要做到高离散,这样会让逆向分析无法理解其确实的意义。
3、加密函数
加密函数主要完成数据加密,变换。使用的指令也会跟正常函数有所不同,比如位操作,byte操作。对于加密函数,最好不要一步完成,不然很容易分析出加密参数,每次将构造的协议交由加密函数处理,做出封包外挂。
  下面用三个游戏来说明
一、B游戏
1、首先对send函数下断点

2、可以看到发送的加密数据(必然加密)

3、向上回溯,寻找加密函数

4、逆向解密函数,或者黑盒调用
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | [cpp] view plain copy print?sub     esp, 8  mov     eax, dword ptr [esp+C]  push    ebx  push    ebp  mov     edx, ecx  mov     ecx, dword ptr [eax]  push    esi  mov     esi, dword ptr [edx+18]  mov     edx, dword ptr [edx+C]  add     eax, 4  mov     ebx, dword ptr [eax+4]  push    edi  mov     edi, dword ptr [esi]  mov     ebp, eax  add     edi, dword ptr [ebp]  mov     ebp, dword ptr [eax+8]  add     ebp, dword ptr [esi+4]  add     eax, 4  add     esi, 8  test    edx, edx  mov     dword ptr [esp+1C], ebx  jbe     short06FE50A7  mov     dword ptr [esp+10], edx  lea     ebx, dword ptr [ebx]  lea     edx, dword ptr [edi+edi+1]        imul    edx, edi  lea     eax, dword ptr [ebp+ebp+1]  imul    eax, ebp  rol     edx, 5  rol     eax, 5  mov     ebx, edx  xor     ebx, ecx  mov     cl, al  xor     eax, dword ptr [esp+1C]  rol     ebx, cl  mov     cl, dl  rol     eax, cl  mov     dword ptr [esp+1C], ebp  mov     ebp, dword ptr [esi]  mov     dword ptr [esp+14], edi  add     eax, dword ptr [esi+4]  mov     ecx, dword ptr [esp+14]  add     ebp, ebx  add     esi, 8  sub     dword ptr [esp+10], 1  mov     edi, eax  jnz     short06FE5060  mov     ebx, dword ptr [esp+1C]  add     ebx, dword ptr [esi+4]  mov     esi, dword ptr [esi]  mov     eax, dword ptr [esp+20]  add     esi, ecx  test    eax, eax  je      short06FE50BA  mov     ecx, dword ptr [eax]  jmp     short06FE50BC  xor     ecx, ecx  mov     edx, dword ptr [esp+24]  xor     ecx, esi  test    eax, eax  mov     dword ptr [edx], ecx  lea     ecx, dword ptr [edx+4]  je      short06FE50D4  add     eax, 4  je      short06FE50D4  mov     edx, dword ptr [eax]  jmp     short06FE50D6  xor     edx, edx  xor     edx, edi  mov     dword ptr [ecx], edx  add     ecx, 4  test    eax, eax  je      short06FE50EA  add     eax, 4  je      short06FE50EA  mov     edx, dword ptr [eax]  jmp     short06FE50EC  xor     edx, edx  xor     edx, ebx  mov     dword ptr [ecx], edx  add     ecx, 4  test    eax, eax  je      short06FE510C  add     eax, 4  je      short06FE510C  mov     eax, dword ptr [eax]  pop     edi  pop     esi  xor     eax, ebp  pop     ebp  mov     dword ptr [ecx], eax  pop     ebx  add     esp, 8  retn    0C | 
  F5之后,还是直接黑盒调用比较方便。
  B游戏的加密比较简单,可以直接构造数据包,然后基于加密算法自己加密后发送至服务器。
二、W游戏
  分析W游戏的TCP部分,可以发现相较B游戏的数据包,数据包结构要严格并且复杂一些。

  但是还是存在一个固定的点可以进行HOOK,存在风险。

三、N游戏实例
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | [cpp] view plain copy print?#ifndef     __COMMON_H_  #define     __COMMON_H_    #include        BOOL_HookApi( unsigned long_My_Addr, unsigned long_Hook_Addr);      /*command 数据*//*向右走*/#define RUN_RIGHT_LEN   0x0e  #define RUN_RIGHT   0x0e,0x00,0xe0,0x55,0x8d,0xe2,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00    /*喊话*///喊话封包的长度不固定-首部为封包长度-然后8个字节的命令,接着4个字节的字符长度,跟到字符串  #define SPECK_ONE   0x00,0x00,0xe0,0x55,0xb9,0x6f,0x00,0x00      #endif    #include    "InjectDll.h"    //BYTE  nCmd[0x0e]={0x0E,0x00,0xe0,0x55,0x91,0x10,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00};  unsigned longMyApi = 0;  unsigned longhookApi = 0;  DWORDdwWrite = 0;  BYTElpResetSend[0x05]={0x8B,0XFF,0X55,0X8B,0XEC};//用于恢复HookSend的5个字节  HINSTANCEhws2_32 = NULL;//ws2_32句柄  HANDLEmy_sendhandle;//保存用于发送send的句柄      intWINAPI DllMain(HANDLEhinstDll, DWORDfdwReason, LPVOIDlpvReserved)  {     // MessageBox( NULL, "yes", "yes", MB_OK);      hModule = hinstDll;      DWORDdwThread;     // UiThread( NULL);     //              switch(fdwReason)      {      caseDLL_PROCESS_ATTACH:          MessageBox( NULL, "Debug", "Debug", MB_OK);          CreateThread( NULL, 0,(unsigned long(__stdcall *)(void*))UiThread,NULL,0,&dwThread);                   break;      caseDLL_THREAD_ATTACH:          break;      caseDLL_THREAD_DETACH:          break;      caseDLL_PROCESS_DETACH:          break;      }           returnTRUE;  }    DWORDWINAPI   UiThread(LPARAMlParam)  {      MSG msg;      HWNDhWnd;      hWnd = CreateDialog( (HINSTANCE)hModule, MAKEINTRESOURCE(IDD_MAIN_PAGE), NULL, MainProc);      ShowWindow( hWnd, SW_SHOW);      UpdateWindow( hWnd);      while(GetMessage(&msg,NULL,0,0))      {       TranslateMessage(&msg);       DispatchMessage(&msg);      }      return0;  }    intCALLBACK MainProc( HWNDhWnd, UINTuMsg, WPARAMwParam, LPARAMlParam)  {            BYTEnCmd[RUN_RIGHT_LEN]={RUN_RIGHT};      HINSTANCEhws2_32;      staticDWORDdwCount[2] = {0x58548565,0x00c45878};      staticHANDLEhFile;      BYTElpBuffer[0x2];      charszFormat[7];      staticDWORDdwWrite = 7;      RtlZeroMemory( lpBuffer,0x2 );      DWORDdwRead;      DWORDlpRead=0;      intnindex=0;      switch( uMsg)      {      caseWM_COMMAND:          switch(wParam)          {          caseIDC_BTN_TEST:                         //  MessageBox( NULL,"debug","debug",MB_OK);             GoRight();        //Speck_Something( "yangzhihao");                              /*                  封包加密call                  重组后send            */                                      //   MessageBox( NULL, "call","call", MB_OK);                            /*__asm{                     pushad                     push 0x0e                     lea  eax, nCmd                     push eax                     mov  eax,0x23ba078                     mov  ecx,eax                     mov  ebx,0x729a00                     mov  eax,0x0e                     call ebx                     popad                 }                       hws2_32 = LoadLibrary( "ws2_32.dll");             (unsigned long)::GetProcAddress( hws2_32, "send");             __asm             {                                  push 0x00                 push 0x0e                 lea  ebx, nCmd                 push ebx                 mov  ebx,my_sendhandle                 push ebx                 call eax             }             */                          break;               caseIDC_READ_TABLE:               MessageBox( NULL, "write", "write", MB_OK);               for( nindex;nindex<0xa7a9;nindex++)                {                                        lpRead = 0x00c45878+nindex;                      ReadProcessMemory( GetCurrentProcess(), (LPVOID)lpRead,lpBuffer, 0x01, &dwRead);                      sprintf( szFormat,"0x%2x",lpBuffer[0]);                      WriteFile( hFile, szFormat, dwWrite, &dwRead, 0);                  }                                break;               caseIDC_BTN_HOOK:                    hws2_32 = LoadLibrary( "ws2_32.dll");                    hookApi = (unsigned long)::GetProcAddress( hws2_32, "send");                    MyApi = (unsigned long)GetSendPara;                    _HOOK_APIN( MyApi, hookApi);                     break;          default:              break;          }          break;            caseWM_INITDIALOG:          hFile = CreateFile( "c:\MYDebugLog.txt",GENERIC_READ | GENERIC_WRITE ,FILE_SHARE_READ|FILE_SHARE_WRITE,              NULL,OPEN_ALWAYS ,FILE_ATTRIBUTE_NORMAL,0);          break;      caseWM_CLOSE:          EndDialog( hWnd, 0);          break;      default:          DefWindowProc( hWnd, uMsg, wParam, lParam);      }      return0;  }            /*取send句柄*/voidGetSendPara(void)  {         __asm       {          /*首先对堆栈进行平衡*/        pop eax          pop eax          pop eax          pop eax                   /*执行判断操作,取send句柄 */        mov eax, dword ptr [esp + 0x0c]          cmp eax,0x0e          jnz JMP_HOOM          mov eax,dword ptr[esp+0x04]          mov my_sendhandle,eax       }       WriteProcessMemory( GetCurrentProcess(), (void*)hookApi, lpResetSend, 0x05, &dwWrite);       dwWrite = 0;       __asm       {            JMP_HOOM:          /*跳回原来的地方*/        sub ebp,0x04          mov eax,hookApi          mov edi,edi          push ebp          mov  ebp,esp          add eax,5          jmp eax          }       return;  }        voidEncode( BYTE* pCmd, intnLen)  {         __asm{                 pushad                 mov  eax,dword ptr[esp+0x34]//长度                 push eax                 mov  eax, dword ptr[esp+0x34]//明文字符序列                 push eax                 mov  eax,0x2fd078 //硬编码                 mov  ecx,eax                 mov  ebx,0x729a00//加密call                 mov  eax,dword ptr[esp+0x08]//长度                 call ebx                 popad               }         return;  }      voidGoRight()  {      BYTElpCmd[RUN_RIGHT_LEN] = {RUN_RIGHT};      Encode( lpCmd,RUN_RIGHT_LEN);            /*发送封包*/    hws2_32 = LoadLibrary( "ws2_32.dll");      (unsigned long)::GetProcAddress( hws2_32, "send");      __asm       {                               push 0x00             push RUN_RIGHT_LEN             lea  ebx, lpCmd             push ebx             mov  ebx,my_sendhandle             push ebx             call eax         }  }    voidSpeck_Something(char* pSpeckBuffer)  {      //MessageBox( NULL, "speck","speck",MB_OK);      BYTEpackLen;    intcount = 0;    WCHARwszSpec[MAX_PATH];    RtlZeroMemory( wszSpec,MAX_PATH*2);        count = strlen( pSpeckBuffer);      longnwLong = MultiByteToWideChar( CP_ACP, 0, pSpeckBuffer, strlen(pSpeckBuffer),                        wszSpec,sizeof(wszSpec));        BYTEtemp[0x08]={SPECK_ONE};      BYTE*lpCmd;      lpCmd = newBYTE[200] ; //申请一块封包的内存      RtlZeroMemory( lpCmd, 200);            memcpy( lpCmd,temp,0x08);    //com封包命令      memcpy( lpCmd,(void*)&count,0x01);        packLen = (BYTE)0x0c+nwLong*2+2;  //包头 封包整个长度    lpCmd[0] = packLen;    lpCmd [0x0A] = (BYTE)nwLong+1;//包的11个字节,字符串长度     memcpy( (lpCmd+12), wszSpec, nwLong*2+1);//把字符放入消息      Encode( lpCmd, (int)lpCmd[0]);        hws2_32 = LoadLibrary( "ws2_32.dll");      (unsigned long)::GetProcAddress( hws2_32, "send");      __asm       {                            push 0x00          push packLen          lea  ebx, lpCmd          push ebx          mov  ebx,my_sendhandle          push ebx          call eax       }       // delete [] lpCmd;        }   | 

