调试环境:

       游戏版本号1.32.8.15801

一、前言

       War3 132重置版内有自带旧版UI界面,包括旧版协议通讯,但是毕竟是新版本,所以略有不同。至于怎么进入旧版模式回头整理完会另外发博文,本文用到的数据包可以通过拦截游戏通讯(UDP协议) 后获取,由于通讯协议众多,本文只针对Host广播的建主信息进行分析、扩展。

War3LuaEx最新版本更新内容_War3LuaEx最新版本更新内容

War3LuaEx最新版本更新内容_ci_02

War3LuaEx最新版本更新内容_旧版_03

二、封包分析

游戏内所有相关操作的通讯包都以下面这个结构开头:

typedef struct _w3x_header {
	byte	bMagic;		//固定头 F7
	byte	bCmd;		//操作码
	WORD	wSize;      //包内数据长度
}W3X_HEADER,*PW3X_HEADER;

这里要分析的封包操作码是 0x30,其结构如下:

typedef struct _w3x_host_gameinfo {
	W3X_HEADER	Header;				//头部
	DWORD		dwHostOs;			//Host操作系统
	DWORD		dwGameType;			//游戏类型
	DWORD		dwGameId;			//游戏ID
	DWORD		dwHostSystemTime;	//Host系统时间
	char		szHostRoomName[16];	//Host建房名称(就是看到的局域网内游戏列表名字),非固定的长度以 \0 结束
	byte		def_0;				//固定为0
	char		szEncodeString[50];	//这段是加密的数据,地图信息,非固定的长度以 \0 结束
	DWORD		dwPlayerMaxNum;		//房间支持总人数
	DWORD		dwGameSortFlag;		//游戏模式 0x01 = 剧情游戏;0x09 = 自定义游戏
	DWORD		dwPlayerNum;		//房间内当前人数
	DWORD		dwHumenSoltNum;		//可进入玩家人数
	DWORD		dwUnknow;			//未知数据
	WORD		wPort;				//Host监听端口
}W3X_HOST_GAMEINFO,*PW3X_HOST_GAMEINFO;

其中有个加密字段,这里提供下解密函数如下:

void DecodeString(IN char* EncodedString,OUT char* DecodedString) {
	char mask;
	int pos = 0, dpos = 0;
	while (EncodedString[pos] != 0)
	{
		if (pos % 8 == 0) mask = EncodedString[pos];
		else
		{
			if ((mask & (0x1 << (pos % 8))) == 0)
				DecodedString[dpos++] = EncodedString[pos] - 1;
			else
				DecodedString[dpos++] = EncodedString[pos];
		}
		pos++;
	}
}

有了解密函数后,将数据解密又得到了个地图信息结构:

typedef struct _w3x_host_mapinfo {
	DWORD		dwMapConfig;		//房间配置
	BYTE		def_0;				//固定0
	WORD		wMapWidth;			//地图宽度
	WORD		wMapHight;			//地图高度
	DWORD		dwMapHash;			//地图Hash,用于校验地图是否用户本地地图是否相同
	char		szMapName[50];		//地图名称,非固定的长度以 \0 结束
	char		szHostName[20];		//建主玩家名字,非固定的长度以 \0 结束
	BYTE		def_1;				//固定0
	char		szSHA1[20];			//地图文件 SHA-1,用于地图下载
	//后面都是缺省,无用数据
}W3X_HOST_MAPINFO,*PW3X_HOST_MAPINFO;

具体的封包数据样本就不发了,总要自己动下手。

三、地图校验分析

       有了以上的结构说明,解析建主信息包还是比较简单的。但是其中有一个数据比较以往版本略有不同,就是MapHash的值,有些小伙伴应该看过开源的war3服务端ghost代码,其中就有对MapHash的计算,在war3 132版本开始这里的算法有了改变。导致ghost的建主地图无法被client认可了,这里逆向看看132版本是怎么算的。

       通过搜索特征 ".w3e"字符串特征,我们会来到这片代码

00007FF75477032D                  83 C5 3A                         add  ebp,0x3A                                        
00007FF754770330                  0F 83 AE 25 00 00                jnc  Warcraft III.00007FF7547728E4                   
00007FF754770336                  81 B6 79 C4 5C 06 41 B8 04 01    xor  dword ptr ds:[rsi+0x065CC479],0x0104B841        
00007FF754770340                  00 00                            add  byte ptr ds:[rax],al                            
00007FF754770342                  48 8D 55 E0                      lea  rdx,qword ptr ss:[rbp-0x20]                     
00007FF754770346                  E8 15 33 79 00                   call  Warcraft III.00007FF754F03660                  
00007FF75477034B                  84 C0                            test  al,al                                          
00007FF75477034D                  0F 84 F2 11 00 00                je  Warcraft III.00007FF754771545                    
00007FF754770353                  48 89 BC 24 20 02 00 00          mov  qword ptr ss:[rsp+0x00000220],rdi               
00007FF75477035B                  48 8D 4D E0                      lea  rcx,qword ptr ss:[rbp-0x20]                     ;War3Map.j
00007FF75477035F                  4C 89 A4 24 18 02 00 00          mov  qword ptr ss:[rsp+0x00000218],r12               
00007FF754770367                  4C 89 AC 24 10 02 00 00          mov  qword ptr ss:[rsp+0x00000210],r13               
00007FF75477036F                  4C 89 B4 24 08 02 00 00          mov  qword ptr ss:[rsp+0x00000208],r14               
00007FF754770377                  4C 89 BC 24 00 02 00 00          mov  qword ptr ss:[rsp+0x00000200],r15               
00007FF75477037F                  E8 1C F5 39 00                   call  Warcraft III.00007FF754B0F8A0                  ;-->>XORRotateLeft2
00007FF754770384                  89 06                            mov  dword ptr ds:[rsi],eax                          
00007FF754770386                  66 66 0F 1F 84 00 00 00 00 00    nop  word ptr ds:[rax+rax+0x00000000]                
00007FF754770390                  72 36                            jc  Warcraft III.00007FF7547703C8                    ;---
00007FF754770392                  8A E4                            mov  ah,ah                                           
00007FF754770394                  73 32                            jnc  Warcraft III.00007FF7547703C8                   
00007FF754770396                  83 C0 BB                         add  eax,0xFFFFFFBB                                  
00007FF754770399                  83 C5 B6                         add  ebp,0xFFFFFFB6                                  
00007FF75477039C                  57                               push  rdi                                            
00007FF75477039D                  F6 D9                            neg  cl                                              
00007FF75477039F                  E8 F9 05 01 00                   call  Warcraft III.00007FF75478099D                  
00007FF7547703A4                  C6 C7 4A                         mov  bh,0x4A                                         
00007FF7547703A7                  81 C5 2A DF 02 CA                add  ebp,0xCA02DF2A                                  
00007FF7547703AD                  80 E9 85                         sub  cl,0x85                                         
00007FF7547703B0                  83 C3 1A                         add  ebx,0x1A                                        
00007FF7547703B3                  F6 DF                            neg  bh                                              
00007FF7547703B5                  81 C2 C1 90 94 E8                add  edx,0xE89490C1                                  
00007FF7547703BB                  6A CE                            push  0xFFFFFFCE                                     
00007FF7547703BD                  81 C6 CA 9C B2 B4                add  esi,0xB4B29CCA                                  
00007FF7547703C3                  4F 83 9C 57 26 48 8B D6 48       sbb  qword ptr ds:[r15+r10*2-0x2974B7DA],0x48        
00007FF7547703CC                  8D 0D B6 C7 BC 01                lea  ecx,dword ptr ds:[0x00007FF75633CB88]           ;Ascii "war3map.w3e"
00007FF7547703D2                  E8 79 FD FF FF                   call  XORRotateLeft                                  
00007FF7547703D7                  48 8B D6                         mov  rdx,rsi                                         
00007FF7547703DA                  48 8D 0D B7 C7 BC 01             lea  rcx,qword ptr ds:[Warcraft III.00007FF75633CB98];Ascii "war3map.wpm"
00007FF7547703E1                  E8 6A FD FF FF                   call  XORRotateLeft                                  
00007FF7547703E6                  48 8B D6                         mov  rdx,rsi                                         
00007FF7547703E9                  48 8D 0D B8 C7 BC 01             lea  rcx,qword ptr ds:[Warcraft III.00007FF75633CBA8];Ascii "war3map.doo"
00007FF7547703F0                  E8 5B FD FF FF                   call  XORRotateLeft                                  
00007FF7547703F5                  48 8B D6                         mov  rdx,rsi                                         
00007FF7547703F8                  48 8D 0D B9 C7 BC 01             lea  rcx,qword ptr ds:[Warcraft III.00007FF75633CBB8];Ascii "war3map.w3u"
00007FF7547703FF                  E8 4C FD FF FF                   call  XORRotateLeft                                  
00007FF754770404                  48 C7 44 24 20 7F FB FF FF       mov  qword ptr ss:[rsp+0x20],0xFFFFFFFFFFFFFB7F      
00007FF75477040D                  48 8B 44 24 20                   mov  rax,qword ptr ss:[rsp+0x20]                     
00007FF754770412                  48 05 C1 05 00 00                add  rax,0x00000000000005C1                          
00007FF754770418                  48 89 44 24 20                   mov  qword ptr ss:[rsp+0x20],rax                     
00007FF75477041D                  4C 8B 64 24 20                   mov  r12,qword ptr ss:[rsp+0x20]

然后通过回溯就可以得到检查本地地图的函数

00007FF754B2E1F8     C3                               retn                                                 
00007FF754B2E1F9     0F B6 4E 30                      movzx  ecx,byte ptr ds:[rsi+0x30]                    
00007FF754B2E1FD     33 D2                            xor  edx,edx                                         
00007FF754B2E1FF     80 E1 01                         and  cl,0x01                                         
00007FF754B2E202     E8 C9 62 7A 00                   call  Warcraft III.00007FF7552D44D0                  
00007FF754B2E207     8B 86 48 01 00 00                mov  eax,dword ptr ds:[rsi+0x00000148]               
00007FF754B2E20D     C1 E8 09                         shr  eax,0x09                                        
00007FF754B2E210     A8 01                            test  al,0x01                                        
00007FF754B2E212     0F 85 E5 00 00 00                jne  Warcraft III.00007FF754B2E2FD                   
00007FF754B2E218     48 8D 4E 54                      lea  rcx,qword ptr ds:[rsi+0x54]                     
00007FF754B2E21C     41 B8 04 01 00 00                mov  r8d,0x00000104                                  
00007FF754B2E222     48 8D 54 24 30                   lea  rdx,qword ptr ss:[rsp+0x30]                     ;MapFileBuffer
00007FF754B2E227     E8 34 86 7B FF                   call  Warcraft III.00007FF7542E6860                  ;GetMapFilePath
00007FF754B2E22C     44 8B 86 48 01 00 00             mov  r8d,dword ptr ds:[rsi+0x00000148]               
00007FF754B2E233     48 8D 46 40                      lea  rax,qword ptr ds:[rsi+0x40]                     
00007FF754B2E237     44 8B 4E 3C                      mov  r9d,dword ptr ds:[rsi+0x3C]                     ;HASH
00007FF754B2E23B     48 8D 54 24 30                   lea  rdx,qword ptr ss:[rsp+0x30]                     ;MapFilePath
00007FF754B2E240     41 81 E0 FF 03 00 00             and  r8d,0x000003FF                                  ;1
00007FF754B2E247     48 89 44 24 20                   mov  qword ptr ss:[rsp+0x20],rax                     ;SHA-1
00007FF754B2E24C     48 8B CF                         mov  rcx,rdi                                         
00007FF754B2E24F     E8 DC A9 C4 FF                   call  Warcraft III.00007FF754778C30                  ;InLocalNetGame_CheckMap
00007FF754B2E254     84 C0                            test  al,al                                          
00007FF754B2E256     0F 84 76 FF FF FF                je  Warcraft III.00007FF754B2E1D2

基本该有的都有了,进过分析基本可以确定目前参与HASH计算的文件与顺序如下:

scripts\\war3map.j
war3map.w3e
war3map.wpm
war3map.doo
war3map.w3u
war3map.w3b
war3map.w3d
war3map.w3a
war3map.w3q

由于Hash是以迭代方式计算,所以文件的顺序会影响结果。其中J文件的计算方式与其他不同,这里直接给出计算代码

DWORD MapFileHashCalc(char *szFile, DWORD dwHash) {
	DWORD dwValue = 0;
	DWORD dwSrcHash = dwHash;
	//读取文件
	HANDLE hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) {
		
		DWORD dwFileSize = GetFileSize(hFile, NULL);
		DWORD dwReadNum = 0;
		byte *szBuffer = new byte[dwFileSize];	
		if (ReadFile(hFile, szBuffer, dwFileSize, &dwReadNum, NULL)) {
			printf("File:%s,Size:%d\r\n", szFile, dwReadNum);

			if (strstr(szFile,".j") != NULL) {
				dwValue = XORRotateLeft(szBuffer, dwReadNum);
				if (dwSrcHash != 0) { 
					dwSrcHash = ROTL((dwValue ^ dwSrcHash), 3); 
				}else {
					dwSrcHash = dwValue;
					printf("=========>%s New Hash = 0x%X\r\n", szFile, dwSrcHash);
				}
			}else {
				int n = dwReadNum / CALC_BLOCK_SIZE;
				for (int i = 0; i < n; i++) {
					dwValue = XORRotateLeft(&szBuffer[i * CALC_BLOCK_SIZE], CALC_BLOCK_SIZE);
					dwSrcHash = ROTL((dwValue ^ dwSrcHash), 3);
				}		
			}
			delete[]szBuffer;
		}	
		CloseHandle(hFile);
	}else {
		printf("!!!OpenFile %s Error[%d]\r\n",szFile, GetLastError());
	}
	return dwSrcHash;
}

PS.由于计算方式的改变,这里地图校验有着一个很大的漏洞,后面看看有没有时间写的DEMO验证一下。