奇迹Mu分析 - 封包协议

1、游戏的主协议


所谓主协议也就是主要的协议类别,对于这款游戏来说可以分为4个类别它们的头分别是:

0xC1:

     * 常用于固定封包格式(例如:移动,NPC对话,使用物品,拾取,普通攻击 等等)

     * 特征:这类封包的长度一般较短

     * 包头结构如下:

_Buffer = Packed Record
    Head:Byte;    //0xC1
    Len:Byte;     //包长度
end;


0xC2:

    * 常用于动态长度封包,常见于返回包(例如:怪物列表,背包列表,伤害包)

    * 特征:这类封包的长度一般较长

    *包头结构如下:

_Buffer = Packed Record
    Head:Byte;    //0xC2
    Len:Word;     //包长度
end;

0xC3:

   *C1的二次加密包

0xC4:

   *C2的二次加密包


2、加密解密

大家看了上面的封包分析也发现了,这款游戏有2次加密,其实就是为了保护一些敏感的数据做的处理,其实后期游戏公司更加的猥琐了下,有部分发包有3次加密。

第一层加密:

这层加密最简单,一般就用于C1包的简单加密,下面就是逆向还原的算法

const
   c_key :array [0..31] of byte = ($AB,$11,$CD,$FE,$18,$23,$C5,$A3,$CA,$33,
                                  $C1,$CC,$66,$67,$21,$F3,$32,$12,$15,$35,
                                  $29,$FF,$FE,$1D,$44,$EF,$CD,$41,$26,$3C,
                                  $4E,$4D);

								  
//加密 参数:IN OUT数据源, IN长度  返回:加密长度
function MyEncode(Buffer: Pointer;Len: Integer): Integer;
var
  Src:pbyte;
  i,j:Integer;
  x:byte;
begin
  Src:=Buffer;
  if Src^ = $C1 then
    begin
      Inc(Src);
      Inc(Src);
      x:=Src^;
      Inc(Src);
      for i := 3 to Len - 1 do
        begin
          j:=i and $8000001F;
          Src^:=Src^ xor x xor c_key[j];
          x:=Src^;
          Inc(Src);
        end;
      Result:=Len;
    end
  else
    Result:=-1;
end;

//解密  参数:IN OUT数据源, IN长度  返回:解密长度
function MyDecode(Buffer: Pointer; Len: Integer): Integer;
var
  Src:array [0..100] of byte;
  i:Integer;
begin
  CopyMemory(@Src,Buffer,Len);
  if Src[0] = $C1 then
    begin
      for i := Len - 1 downto 3 do
        begin
          Src[i]:=Src[i] xor Src[i-1] xor c_key[i];
        end;
      Result:=Len;
      CopyMemory(Buffer,@Src,Len);
    end
  else
    Result:=-1;
end;

第二层加密 :

即是C1完成加密后的变成C3的2次加密,由于加密过程被VM了,这里就提供下逆向片段

00605FFD   0F8D F3010000    JGE main.006061F6
00606003   33C0             XOR EAX,EAX
00606005   0F85 EB010000    JNZ main.006061F6
0060600B   8B85 5CE3FDFF    MOV EAX,DWORD PTR SS:[EBP+FFFDE35C]
00606011   40               INC EAX
00606012   40               INC EAX
00606013   8985 54CBFDFF    MOV DWORD PTR SS:[EBP+FFFDCB54],EAX
00606019   C685 64FBFDFF C3 MOV BYTE PTR SS:[EBP+FFFDFB64],0C3
00606020   8A85 54CBFDFF    MOV AL,BYTE PTR SS:[EBP+FFFDCB54]
00606026   8885 65FBFDFF    MOV BYTE PTR SS:[EBP+FFFDFB65],AL
0060602C   8B85 0CCBFDFF    MOV EAX,DWORD PTR SS:[EBP+FFFDCB0C]
00606032   2B85 58CBFDFF    SUB EAX,DWORD PTR SS:[EBP+FFFDCB58]
00606038   50               PUSH EAX                                    //InLen     需要加密的长度
00606039   8B85 58CBFDFF    MOV EAX,DWORD PTR SS:[EBP+FFFDCB58]
0060603F   8D8405 5CCBFDFF  LEA EAX,DWORD PTR SS:[EBP+EAX+FFFDCB5C]
00606046   50               PUSH EAX                                    //InBuffer  加密的数据源
00606047   8D85 66FBFDFF    LEA EAX,DWORD PTR SS:[EBP+FFFDFB66]
0060604D   50               PUSH EAX                                    //OutBuffer 加密后存放的缓存
0060604E   FF35 5C2E2301    PUSH DWORD PTR DS:[1232E5C]              ;  //KeyTable  默认变量
00606054   B9 1C6FBB08      MOV ECX,main.08BB6F1C                       //类指针
00606059   E8 B28C4F00      CALL main.00AFED10                       ;  pC3EnCode 
0060605E   A1 5C2E2301      MOV EAX,DWORD PTR DS:[1232E5C]
00606063   8985 2CCBFDFF    MOV DWORD PTR SS:[EBP+FFFDCB2C],EAX
00606069   8B85 54CBFDFF    MOV EAX,DWORD PTR SS:[EBP+FFFDCB54]
0060606F   8985 38CBFDFF    MOV DWORD PTR SS:[EBP+FFFDCB38],EAX
这里给出这个函数的类型

Type
   FEnCodeC3 = function (DefBase:Cardinal;OutBuffer:Pointer;InBuffer:Pointer;InLen:Integer):Integer;Stdcall;

第三层加密:

这个加密其实是基于1,2层中间的,是根据不同的包查询获得虚函数进行的加密,所以在这里提供一个查询的后调用虚函数的地方,给大家参考

00780522   E8 AC200000      CALL main.007825D3
00780527   0FB6C0           MOVZX EAX,AL
0078052A   85C0             TEST EAX,EAX
0078052C   74 02            JE SHORT main.00780530
0078052E   EB 1F            JMP SHORT main.0078054F
00780530   8D4D F4          LEA ECX,DWORD PTR SS:[EBP-C]
00780533   E8 76200000      CALL main.007825AE
00780538   8B40 04          MOV EAX,DWORD PTR DS:[EAX+4]
0078053B   8945 FC          MOV DWORD PTR SS:[EBP-4],EAX
0078053E   FF75 10          PUSH DWORD PTR SS:[EBP+10]
00780541   FF75 0C          PUSH DWORD PTR SS:[EBP+C]
00780544   8B45 FC          MOV EAX,DWORD PTR SS:[EBP-4]
00780547   8B00             MOV EAX,DWORD PTR DS:[EAX]
00780549   8B4D FC          MOV ECX,DWORD PTR SS:[EBP-4]
0078054C   FF50 04          CALL DWORD PTR DS:[EAX+4]               //这里就是查表后得到的3次加密处理
0078054F   C9               LEAVE
00780550   C2 0C00          RETN 0C
00780553   55               PUSH EBP
00780554   8BEC             MOV EBP,ESP
00780556   83EC 18          SUB ESP,18
00780559   894D E8          MOV DWORD PTR SS:[EBP-18],ECX
0078055C   8D45 08          LEA EAX,DWORD PTR SS:[EBP+8]
0078055F   50               PUSH EAX
00780560   8D45 F4          LEA EAX,DWORD PTR SS:[EBP-C]
00780563   50               PUSH EAX
00780564   8B4D E8          MOV ECX,DWORD PTR SS:[EBP-18]
这里给出这个函数的通用类型

Type
   FParamEncode = Procedure (var Buffer;Len:Integer);Stdcall;

3、返回包的分析

这款游戏的返回包中C1,C2头的是明文数据,没有任何加密,C3和C4则是2次加密。返回包中没有用到第3次加密!返回包是以粘连方式返回,如果HOOK Api Recv需自行拆分封包。




你可能感兴趣的:(那些年我们玩过的网游,奇迹,MU,封包,壮游,辅助)