网络游戏协议封包需注意点

发表于2016-09-01
评论0 3.8k浏览
  对于强客户端的游戏,就有很大可能出现脱机挂,因为一切客户端的计算、处理逻辑都不需要进行,只需要将最理想的作弊数据包直接发给服务器,就能达到游戏收益最大化。
下面这几点是需要多加注意的点。

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     short 06FE50A7 
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     short 06FE5060 
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      short 06FE50BA 
mov     ecx, dword ptr [eax] 
jmp     short 06FE50BC 
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      short 06FE50D4 
add     eax, 4 
je      short 06FE50D4 
mov     edx, dword ptr [eax] 
jmp     short 06FE50D6 
xor     edx, edx 
xor     edx, edi 
mov     dword ptr [ecx], edx 
add     ecx, 4 
test    eax, eax 
je      short 06FE50EA 
add     eax, 4 
je      short 06FE50EA 
mov     edx, dword ptr [eax] 
jmp     short 06FE50EC 
xor     edx, edx 
xor     edx, ebx 
mov     dword ptr [ecx], edx 
add     ecx, 4 
test    eax, eax 
je      short 06FE510C 
add     eax, 4 
je      short 06FE510C 
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 long   MyApi = 0; 
unsigned long   hookApi = 0; 
DWORD     dwWrite = 0; 
BYTE      lpResetSend[0x05]={0x8B,0XFF,0X55,0X8B,0XEC};//用于恢复HookSend的5个字节 
HINSTANCE hws2_32 = NULL;//ws2_32句柄 
HANDLE    my_sendhandle;//保存用于发送send的句柄 
   
   
int WINAPI DllMain(HANDLE hinstDll, DWORD fdwReason, LPVOID lpvReserved) 
   // MessageBox( NULL, "yes", "yes", MB_OK); 
    hModule = hinstDll; 
    DWORD   dwThread; 
   // UiThread( NULL); 
   //  
   
      
    switch(fdwReason) 
    
    case DLL_PROCESS_ATTACH: 
        MessageBox( NULL, "Debug", "Debug", MB_OK); 
        CreateThread( NULL, 0,(unsigned long (__stdcall *)(void *))UiThread,NULL,0,&dwThread); 
          
        break
    case DLL_THREAD_ATTACH: 
        break
    case DLL_THREAD_DETACH: 
        break
    case DLL_PROCESS_DETACH: 
        break
    
      
    return TRUE; 
   
DWORD    WINAPI   UiThread(LPARAM lParam) 
    MSG msg; 
    HWND hWnd; 
    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); 
    
    return 0; 
   
int CALLBACK MainProc( HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM lParam) 
       
    BYTE  nCmd[RUN_RIGHT_LEN]={RUN_RIGHT}; 
    HINSTANCE hws2_32; 
    static  DWORD dwCount[2] = {0x58548565,0x00c45878}; 
    static  HANDLE hFile; 
    BYTE    lpBuffer[0x2]; 
    char    szFormat[7]; 
    static  DWORD dwWrite = 7; 
    RtlZeroMemory( lpBuffer,0x2 ); 
    DWORD dwRead; 
    DWORD lpRead=0; 
    int     nindex=0; 
    switch( uMsg) 
    
    case WM_COMMAND: 
        switch(wParam) 
        
        case IDC_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
             case    IDC_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
             case IDC_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
       
    case WM_INITDIALOG: 
        hFile = CreateFile( "c:\MYDebugLog.txt",GENERIC_READ | GENERIC_WRITE ,FILE_SHARE_READ|FILE_SHARE_WRITE, 
            NULL,OPEN_ALWAYS ,FILE_ATTRIBUTE_NORMAL,0); 
        break
    case WM_CLOSE: 
        EndDialog( hWnd, 0); 
        break
    default
        DefWindowProc( hWnd, uMsg, wParam, lParam); 
    
    return 0; 
   
   
   
   
   
/*取send句柄*/ 
void    GetSendPara(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
   
   
   
void    Encode( BYTE* pCmd, int nLen) 
       __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
   
   
void    GoRight() 
    BYTE lpCmd[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 
       
   
void    Speck_Something(char* pSpeckBuffer) 
    //MessageBox( NULL, "speck","speck",MB_OK); 
    BYTE  packLen; 
  int count = 0; 
  WCHAR  wszSpec[MAX_PATH]; 
  RtlZeroMemory( wszSpec,MAX_PATH*2); 
   
    count = strlen( pSpeckBuffer); 
   
  long nwLong = MultiByteToWideChar( CP_ACP, 0, pSpeckBuffer, strlen(pSpeckBuffer), 
                      wszSpec,sizeof(wszSpec)); 
   
    BYTE    temp[0x08]={SPECK_ONE}; 
    BYTE    *lpCmd; 
    lpCmd = new BYTE [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; 
       

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引