[原创]某MMORPG服务器端的研究

苦逼热狗

路边通讯社社长
VIP
注册
2002-10-12
消息
47,114
荣誉分数
2,376
声望点数
393
作者:变态热狗
Email:torune@hotmail.com

注解:乘现在我还记得去年干过什么,我把天堂服务器端的研究,扩充之类的技术文档整理一下

天堂3章的服务器端是从2章来的,但是这些新功能大家又是如何添加的呢。

请听我一步一步道来

1.DLL HOOK

DLL HOOK有很多种方法来实现,不过主要是修改程序header,通过改变section,来满足
这里有个更简单一点的方法
利用KERNEL32.LoadLibraryA("DLLNAME.dll");
修改Entry point附近的代码,找个空余的地方,做个简单的跳转

先还原因为jmp抹去的代码
然后

push dll_name_string_offset
call loadlibrary_function_offset ;ollydbg可以通过查找所有内部模块调用来得到KERNEL32.LoadLibraryA的offset
test eax, eax
jnz 来的地方
int3 ;unable to load the dll
 
2.利用汇编与C++来实现原有function的修改
代码:
	//mov ecx
	//jmp ecx
	BYTE JumpCode3[] = {0xB9, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE1}; // 跳转代码 

// 设置代码跳转 
void SetCodeJump3(DWORD dwDesAddr, DWORD dwSrcAddr)
{ 
	DWORD flOldProtect; 
	VirtualProtect((LPVOID)dwSrcAddr, 7, PAGE_EXECUTE_READWRITE, &flOldProtect); 
	*(DWORD*)(JumpCode3 + 1) = dwDesAddr; 
	memcpy((void*)dwSrcAddr, (const void*)JumpCode3, 7); 
}
一个简单的代码跳转替换演示代码
原理很简单
给一个offset
然后把
mov ecx, function_offset_in_dll
jmp ecx
写到程序内存中去

//调用的例子
SetCodeJump3((DWORD)AssembleLoadCharacter, 0x00465D00);

代码:
void __declspec(naked) AssembleLoadCharacter()
{
	__asm
	{
		push [EBP-0B8h];
		LEA ECX, [EBP-18h]
		mov eax, 00454F90h
		call eax;
		push eax;
		push [EBP-28h];
		push [EBP-1Ch];
		call AssemLoadCharacter;
		push 00465F00h;
		ret;
	}
}
这个_asm代码的关键就在
__declspec(naked)
使用这个以后,生成的function不会自己去保存ecx/eax一类的东西,他只会生成那一段汇编码

AssemLoadCharacter是我写的一段C++代码用来替代原有的function
毕竟2章的内容跟3章的内容有不少区别
代码:
void __stdcall AssemLoadCharacter(int TotalCount, int lBinarySize, BYTE* pSocket, BYTE* pData)
{
	BYTE *pLoadCharacter, *pLoadCharacterData, *pBuffer;
	int i, nCharID, dwBuffSize, ClassID;

	pBuffer = pData;	
	dwBuffSize = lBinarySize + ( 17 * TotalCount );
	pLoadCharacter = (BYTE *)GlobalAlloc(GMEM_ZEROINIT, dwBuffSize);
	pLoadCharacterData = pLoadCharacter;
	i = 0;
	dwBuffSize = 0;
	while (i < TotalCount)
	{
		while ((*(unsigned short*)pBuffer) != 0)
		{
			*(unsigned short*)pLoadCharacter = *(unsigned short*)pBuffer;//Char_Name 
			pLoadCharacter += 2;
			pBuffer += 2;
			dwBuffSize+=2;
		}
		pLoadCharacter += 2;
		pBuffer += 2;
		dwBuffSize+=2;
		nCharID = *(unsigned int*)pBuffer;//Char_ID ;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_ID 
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		while ((*(unsigned short*)pBuffer) != 0)
		{
			*(unsigned short*)pLoadCharacter = *(unsigned short*)pBuffer;//Login_Name 
			pLoadCharacter += 2;
			pBuffer += 2;
			dwBuffSize+=2;
		}
		pLoadCharacter += 2;
		pBuffer += 2;
		dwBuffSize+=2;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Session_ID 
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Clan_ID 
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//unk
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Sex
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Race
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		ClassID = *(unsigned int*)pBuffer;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Class_ID
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//unk2
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//x
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//y
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//z
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Current_Hp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Current_Hp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Current_Mp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Current_Mp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Sp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Exp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Level
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//0x00
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_UNDER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_REAR
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_LEAR
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_NECK
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_RFINGER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_LFINGER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_HEAD
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_RHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_LHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_GLOVES
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_CHEST
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_LEGS
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_FEET
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_BACK
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Object Inventory.PAPERDOLL_LRHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		pLoadCharacter += 4;									//Object Inventory.PAPERDOLL_HAIR		
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_UNDER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_REAR
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_LEAR
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_NECK
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_RFINGER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_LFINGER
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_HEAD
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_RHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_LHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_GLOVES
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_CHEST
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_LEGS
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_FEET
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_BACK
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Item Inventory.PAPERDOLL_LRHAND
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		pLoadCharacter += 4;									//Item Inventory.PAPERDOLL_HAIR
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_HairStyle
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_HairColor
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_Face
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_MaxHp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_MaxHp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_MaxMp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_MaxMp
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = *(unsigned int*)pBuffer;//Char_DeleteTimer
		pLoadCharacter += 4;
		pBuffer += 4;
		dwBuffSize+=4;
		*(unsigned int*)pLoadCharacter = ClassID;				 //Char_ClassID
		pLoadCharacter += 9;
		dwBuffSize += 9;
		*(unsigned int*)(pSocket + i * 4 + 0x13f0) = nCharID;
		i++;
	}

	*(unsigned int*)(pSocket + 0x15F4) = TotalCount;
	Lin2Send(pSocket, "cdb", 0x1f, TotalCount, dwBuffSize, pLoadCharacterData);
	GlobalFree(pLoadCharacterData);
}
 
3.调用可执行程序里头的function
当你需要重复的调用在原程序里头的某个function的时候
_asm
{
push 123;
call math_function_offset;
ret;
}
你需要这一类的代码
代码:
	typedef DWORD (__cdecl * LIN2SEND)(BYTE* pSocket, ...);
	LIN2SEND Lin2Send = (LIN2SEND)0x004DFC70;
//调用的例子
Lin2Send(pSocket, "cdb", 0x1f, TotalCount, dwBuffSize, pLoadCharacterData);
在这里我们只需要make sure第一个argument是正确的
也就是说如果我们这么写的话
typedef DWORD (__cdecl * LIN2PRINT)(...);
LIN2PRINT Print = (LIN2PRINT)0x0053EEE0;
你随便写啥,只要你确定你的argument(s)在源程序那边行得通的话,一切okey

天堂服务器端的发送/接受系统挺好玩的
她是按照一个char array
一个一个字符来判断要发送啥,或者要接受啥
c = char
d = digital = int
b = binary = char array,如果是binary的话,需要在前头附加一个buffer size
例如例子里头这个
Lin2Send(pSocket, "cdb", 0x1f, TotalCount, dwBuffSize, pLoadCharacterData);
Lin2Send(Socket, String格式, 指令index, int, buff size, pointer to buffer );
 
4.新功能的扩充
每个版本的程序或多或少会有点不同,新的指令要如何让服务器懂得我们在说啥呢

天堂的服务器端跟客户端之间的所有数据交流都需要解密,然后才能读取
这样其实帮了我们一个大忙

我们只要找到加密/解密函数的位置
typedef DWORD (__cdecl * ENCRYPTOLD)(...);
typedef DWORD (__cdecl * DECRYPTOLD)(...);
ENCRYPTOLD EncrypOld = (ENCRYPTOLD)0x004DF3E0; //旧的加密函数
DECRYPTOLD DecrypOld = (DECRYPTOLD)0x00500FF0; //旧的解密函数

然后替换为我们的内容
代码:
//新的解密函数过程
void __cdecl Decrypt(BYTE *pData, BYTE *pKey, DWORD dwLen)
{
	DecrypOld(pData, pKey, dwLen);
	RecvProc(pData);
}

//新的加密函数过程
void __cdecl Encrypt(BYTE *pData, BYTE *pKey, DWORD dwLen)
{
	SendProc(pData);
	EncrypOld(pData, pKey, dwLen);
}
这样,一方面,我们可以有选择性的过滤一些指令,修改/阻止/调试都更方便了
代码:
void __cdecl RecvProc(BYTE * pData) 
{
	
	DWORD opCode;
	BYTE* pBuffer,*pUser,*pSocket;
	//得到用户CSocket对象指针
	__asm
	{
		mov pSocket,edi;
		mov eax,[edi+1394h];
		mov pUser,eax;
	}
	pBuffer = pData;
	opCode = *pBuffer & 0xFF;
	pBuffer++;

	//Print(0x18AA81D8, 0, L"opCode [%x]", opCode);
	switch(opCode)
	{		
		case 0x01:
			MoveBackwardToLocation(pBuffer, pUser,pSocket); 
			break;
		//case 0x03:	
			//CreateUserinfoEx(pUser, pSocket); 
			//break;	
		case 0x0a:
			AttackRequest(pBuffer, pUser,pSocket); 
			break;
		case 0x0b:	
			RequestCharacterCreate(pBuffer);	
			break;		
		case 0x11:	
			RequestUnequipItem(pBuffer,pUser,pSocket); 
			break;
		/*case 0x12:
			RequestDropItem(pBuffer);	
			break;*/
		case 0x14:	
			UseItem(pBuffer,pUser,pSocket);	
			break;
		case 0x20:	
			*(wchar_t*)(pBuffer + 200) = 0;	
			break;		 
		case 0x21:	
			RequestBypassToServer(pBuffer,pUser,pSocket);	
			break;
		case 0x29:	
			RequestJoinParty(pBuffer);	
			break;
		case 0x2f:	
			RequestMagicSkill(pBuffer,pUser,pSocket);	
			break;
		case 0x45:
			RequestActionUse(pBuffer,pUser,pSocket);
			break;
		case 0x48:	
			ValidatePosition(pBuffer,pUser,pSocket);	
			break;
		case 0x55:
			RequestGiveNickName(pBuffer);
			break;
		case 0x57:	
			RequestShowBoard(pSocket,pBuffer);	
			break;
		case 0x59:
			RequestDestroyItem(pBuffer,pSocket,pBuffer);	
			break;
		case 0xaa:	
			RequestUserCommand(pBuffer,pUser,pSocket);	
			break;
		case 0xae:
			RequestRecipeItemMakeInfo(pBuffer,pUser,pSocket);
			break;
		case 0xaf:
			RequestRecipeItemMakeSelf(pBuffer,pUser,pSocket);
			break;
		case 0xb1:
			RequestRecipeShopMessageSet(pBuffer,pUser,pSocket);
			break;
		case 0xb2:
			RequestRecipeShopListSet(pBuffer,pUser,pSocket);
			break;
		case 0xb5:
			RequestRecipeShopMakeInfo(pBuffer,pUser,pSocket);
			break;
		case 0xb7:
		//	RequestRecipeShopMakeInfo(pBuffer,pUser,pSocket);
			break;
		case 0xb8:	
			ObservationReturn(pBuffer,pUser,pSocket);	
			break;
		case 0xb9:	
			Commend(pBuffer,pUser,pSocket);
			break;
		case 0xba:	
			RequestHennaEquipList(pBuffer,pUser,pSocket);
			break;
		case 0xbb:	
			RequestHennaItemInfo(pBuffer,pUser,pSocket);
			break;
		case 0xbc:	
			RequestHennaEquip(pBuffer,pUser,pSocket);
			break;
		case 0xbd:
			RequestHennaUnequipList(pBuffer,pUser,pSocket);
			break;
		case 0xbe:
			RequestUnequipInfo(pBuffer,pUser,pSocket);
			break;
		case 0xbf:
			RequestHennaUnequip(pBuffer,pUser,pSocket);
			break;
		case 0xc0:
			RequestPledgePower(pBuffer,pUser,pSocket);
			break;
		case 0xc1:
			RequestMakeMacro(pBuffer,pUser,pSocket);
			break;
		case 0xc2:
			RequestDeleteMacro(pBuffer,pUser,pSocket);
			break;
		case 0xcd:			
			Lin2Send(pSocket, "cd", 0xB6, 1665);
			break;
		case 0xd0:	
			Request(pBuffer,pUser,pSocket);	
			break;
		default:				
			//Print(0x18AA81D8, 0, L"opCode [%x]", opCode);
			if (*(pData) > 0xa8)	*(pData) = 0xa8;	
			break;
	}
}
void __cdecl SendProc(BYTE * pData) 
{
	
	BYTE* pSocket;
	BYTE* pUser;
	BYTE* pBuffer;
	DWORD opCode;
	
	//得到用户CSocket对象指针
	__asm
	{
		mov pSocket,edi;
		mov eax,[edi+1394h];
		mov pUser,eax;
	}

	pBuffer = pData;
	opCode = *pBuffer & 0xFF;
	pBuffer++;	
	//Print(0x18AA81D8, 0, L"S-C opCode [%x]", opCode);
	switch(opCode)
	{
		//case 0x04:	Userinfo(pBuffer, pUser, pSocket); 	break;
		case 0x15:	CreateUserinfoEx(pUser, pSocket); break;	
		case 0x1b:	ItemList(pBuffer, pUser, pSocket); break;
	}
}
 
5.数据库方面的扩展
天堂数据库的操作跟客户端的操作是分开2个程序
个人感觉ADO会比ODBC更好控制执行速度
我们如果像扩充新的指令呢?
方法类似

必要的header

#pragma warning (disable: 4146)
#pragma warning (disable: 4530)
#import "C:\Program files\Common Files\System\Ado\msado15.dll" rename("EOF", "ADOEOF")
#pragma warning (default: 4146)


同时在entrypoint添加数据库连接
以及函数钩子
代码:
		try{

			pConn.CreateInstance(__uuidof(ADODB::Connection));
			pConn->CursorLocation = ADODB::adUseClient;			 
			pConn->Open(L"Provider=sqloledb;Data Source=(local);"
							 L"Initial Catalog=lin2world;"
							 L"Integrated Security=SSPI;", L"",
							 L"", ADODB::adConnectUnspecified);
		}
		catch(...)
		{ 
			Print(0x026849B8, 2, L"Unable to Connect To SQL Server");
			return 0;
		}
		
		//Extend((DWORD)sFormat_0, 0x00459A9A);
		DecrypOld = (DECRYPTOLD)SetFunctionHook(0x0043C418, (DWORD)Decrypt);


//---------------
// 设置函数钩子
FARPROC __inline SetFunctionHook(DWORD dwAddress, DWORD dwNewAddr)
{
	DWORD dwOldAddr = 0;
	DWORD flOldProtect;

	// 开始设置函数钩子
	VirtualProtect((LPVOID)dwAddress, 4, PAGE_EXECUTE_READWRITE, &flOldProtect);
	dwOldAddr = dwAddress + 4 + *(DWORD*)dwAddress;
	*(DWORD*)dwAddress = dwNewAddr - (dwAddress + 4);

	return (FARPROC)dwOldAddr;
}
void __stdcall RecvProc(BYTE *pData)
{

	BYTE *pBuffer, *pSocket;
	DWORD opCode;	

	//得到用户CSocket对象指针
	__asm	mov pSocket,esi;

	pBuffer = pData;
	opCode = *pBuffer & 0xFF;
	pBuffer++;	
	//Print(0x026849B8, 0, L"SEVER-CACHE opCode [%x]", opCode);
	if (opCode > 0xe0)	*(pData) = 0x04;
	switch(opCode)
	{
		case 0xE1:	RequestAddMacro(pBuffer, pSocket);		break;
		case 0xE2:	RequestSendMacros(pBuffer, pSocket);	break;
		case 0xE3:	RequestDeleteMacro(pBuffer, pSocket);	break;
		case 0xE4:	RequestSaveClanPrivileges(pBuffer, pSocket);	break;
		case 0xE5:	RequestSaveHairGear(pBuffer, pSocket);	break;
		case 0xE6:  RequestSaveCP(pBuffer, pSocket);	break;
		case 0xE7:  RequestSaveCommend(pBuffer, pSocket);	break;
		case 0xE8:  RequestCharData(pBuffer, pSocket);	break;
		case 0xE9:  RequestAddRecipeBook(pBuffer, pSocket);	break;
		case 0xEA:  RequestRecipeBookOpen(pBuffer, pSocket);	break;
		case 0xEB:  RequestRemoveHenna(pBuffer, pSocket);	break;
		case 0xEC:  RequestDrawHenna(pBuffer, pSocket);	break;
		/*
		case 0xF1:	
			Lin2Send(pSocket, "cd", 0xf1,1);	
			*(pData) = 0x04;
			break;
		case 0xF2:	SqlHammer(pBuffer, pSocket);	break;
		*/
	}
}
//新的解密函数过程
void __cdecl Decrypt(BYTE *pData, BYTE *pKey, DWORD dwExtra, DWORD dwLen)
{
	__asm
	{
		pushad;
		push pData;
		call RecvProc;
		popad;
	}
}

简单的写数据,不返回数据的处理的例子

代码:
HRESULT ExecuteSQL(const wchar_t* szUnicodeString)
{
	try
	{		
		pConn->Execute(szUnicodeString, NULL, ADODB::adCmdText);
		return S_OK;
	}catch(...)
	{
		return 1;
	}
}

void __stdcall RequestSaveCP(BYTE *pData, BYTE *pSocket)
{
	DWORD dwCharID, CurrentCP, MaxCP;
	BYTE *pBuffer;
	wchar_t *pStatement;

	*(pData - 1) = 0x04;
	pBuffer = pData;
	dwCharID = *(unsigned int*)pBuffer;
	pBuffer+=4;
	CurrentCP = *(unsigned int*)pBuffer;
	pBuffer+=4;
	MaxCP = *(unsigned int*)pBuffer;

	pStatement = (wchar_t *)GlobalAlloc(GMEM_ZEROINIT, 256);
	wsprintfW(pStatement, L"UPDATE user_data SET CurrentCP = %d, MaxCP = %d WHERE (char_id = %d)", CurrentCP, MaxCP, dwCharID);	
	ExecuteSQL(pStatement);
	GlobalFree(pStatement);
}

再来个复杂点的例子
代码:
HRESULT EXECAddMacro(const wchar_t* pStatement, int dwCharID, int dwID, wchar_t **pName, wchar_t **pDesc, wchar_t **pAcronym, int nIcon, int nCount, long lBinarySize, BYTE* pBufEx)
{
	ADODB::_RecordsetPtr myRecordset;
	HRESULT hr;	
	VARIANT varChunk;	

	BYTE *buf;
	SAFEARRAY *psa;
	SAFEARRAYBOUND rgsabound[1]; 
	
	rgsabound[0].cElements = lBinarySize; 
	rgsabound[0].lLbound = 0;
	psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
	buf = (BYTE *)GlobalAlloc(GMEM_ZEROINIT, lBinarySize);
	try
	{
		SafeArrayAccessData(psa, (void **)&buf);
		for(long index=0;index<(long)lBinarySize;index++)
		{
			buf[index]=pBufEx[index];
		}
		SafeArrayUnaccessData(psa);

		varChunk.vt = VT_ARRAY | VT_UI1;
		varChunk.parray = psa;

		myRecordset.CreateInstance(__uuidof(ADODB::Recordset));
		hr = myRecordset->Open(pStatement,
					pConn.GetInterfacePtr(), 
					ADODB::adOpenKeyset,
					ADODB::adLockOptimistic, 
					ADODB::adCmdText);
		if (hr == S_OK)
		{			
			if(!myRecordset->ADOEOF)
			{
				myRecordset->Fields->GetItem( _variant_t( 3L ) )->Value= _bstr_t(*pName);
				myRecordset->Fields->GetItem( _variant_t( 4L ) )->Value= _bstr_t(*pDesc);
				myRecordset->Fields->GetItem( _variant_t( 5L ) )->Value= _bstr_t(*pAcronym);
				myRecordset->Fields->GetItem( _variant_t( 6L ) )->Value= _variant_t((long)nIcon);
				myRecordset->Fields->GetItem( _variant_t( 7L ) )->Value= _variant_t((long)nCount);
				myRecordset->Fields->GetItem( _variant_t( 8L ) )->AppendChunk(varChunk); 	
				myRecordset->Update(); 
			}
			else
			{
				myRecordset->AddNew(); 
				myRecordset->PutCollect("char_id", _variant_t((long)dwCharID));
				myRecordset->PutCollect("id",  _variant_t((long)dwID));
				myRecordset->PutCollect("name", _bstr_t(*pName));
				myRecordset->PutCollect("descr", _bstr_t(*pDesc));
				myRecordset->PutCollect("acronym", _bstr_t(*pAcronym));
				myRecordset->PutCollect("icon", _variant_t((long)nIcon));
				myRecordset->PutCollect("mcount", _variant_t((long)nCount));
				myRecordset->Fields->GetItem("commands")->AppendChunk(varChunk); 	
				myRecordset->Update(); 
			}
			myRecordset->putref_ActiveConnection(NULL);
			myRecordset->Close();
			myRecordset = NULL;
			hr = S_OK;
		}
		else
		{
			myRecordset->putref_ActiveConnection(NULL);
			myRecordset->Close();
			myRecordset = NULL;
			hr = 1;
		}
		
	}catch(...)
	{
		myRecordset->putref_ActiveConnection(NULL);
		myRecordset->Close();
		myRecordset = NULL;
		hr = 1;
	}
	
	GlobalFree(buf);
	VariantClear(&varChunk);
	SafeArrayDestroyData(psa);
	return hr;
}
 
附送一个天堂4章Cache注册机的代码
当初花了点时间研究了一下他的加密方法
其实最恶心的是,5个bits,5个bits得处理方法
我靠,无耻啊

只需要电脑名字,跟网卡地址
sprintf(pStr,"[%s]:[NC]:[%s]", pComputerName, pMacAddress);
CString str(pStr);
KeyGenerate(str);
代码:
void CCacheSNGenDlg::KeyGenerate(CString keyString)
{
		
	char CryptStr[] = {'B','D','E','F','G','I','J','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9'};
	long BinaryStr[] = {
		0x00,	//00000	//B
		0x10,	//10000	//D
		0x08,	//01000	//E
		0x18,	//11000	//F
		0x04,	//00100	//G
		0x14,	//10100	//I
		0x0C,	//01100	//J
		0x1C,	//11100	//L
		0x02,	//00010	//M
		0x12,	//10010	//N
		0x0A,	//O
		0x1A,	//P
		0x06,	//Q
		0x16,	//R
		0x0E,	//S
		0x1E,	//T
		0x01,	//U
		0x11,	//V
		0x09,	//W
		0x19,	//X
		0x05,	//Y
		0x15,	//Z
		0x0D,	//0
		0x1D,	//1
		0x03,	//2
		0x13,	//3
		0x0B,	//4
		0x1B,	//5
		0x07,	//6
		0x17,	//7
		0x0F,	//8
		0x1F,	//11111	//9
		0
	};

	int mask[] = {
		0x03,
		0x0F,
		0x3F,
		0xFF
	};

	char *c_id = new char[64];
	long *lid = new long[5];
	long *l_id = new long[5];

	long *lSerial = new long[30];

	CString encryptKey;
	HCRYPTPROV cspContext;
	HCRYPTHASH hashData;

	DWORD dwHashLen; 
	BYTE *pbHash; 

	int i,j;
	//FILE *fp;
	//fp=fopen("mydata.txt","a");

	encryptKey=keyString;
	CryptAcquireContextW(&cspContext,NULL,NULL,1 ,0);
	CryptCreateHash(cspContext,0x8003,0,0,&hashData);
	CryptHashData(hashData,(const unsigned char *)encryptKey.GetBuffer(256),encryptKey.GetLength()+1,0);
	CryptGetHashParam(hashData,2,NULL,&dwHashLen,0);
	pbHash = (BYTE*)malloc(dwHashLen);
	CryptGetHashParam(hashData,HP_HASHVAL,pbHash,&dwHashLen,0);

	for (j = 0; j < 4; j++)
	{		
		lid[j]  = pbHash[ j * 4 + 0 ]	<<	24;
		lid[j] |= pbHash[ j * 4 + 1 ]	<<	16;
		lid[j] |= pbHash[ j * 4 + 2 ]	<<	8;
		lid[j] |= pbHash[ j * 4 + 3 ];

		l_id[j] = 0;
		long ref = lid[j];
		for(int i = 0; i < 32; i++)
		{
			if(ref & 1)
				l_id[j] |= 1 << (31 - i);
			ref >>= 1;
		}
	}

	/////////////////////////////
	long lSerialN = l_id[0];
	for (int n = 0; n < 5; n++)
	{

		lSerial[0] = ( lSerialN >> 0 ) & 0x1F;
		lSerial[1] = ( lSerialN >> 5 ) & 0x1F;
		lSerial[2] = ( lSerialN >> 10 ) & 0x1F;
		lSerial[3] = ( lSerialN >> 15 ) & 0x1F;
		lSerial[4] = ( lSerialN >> 20 ) & 0x1F;
		lSerial[5] = ( lSerialN >> 25 ) & 0x1F;

		for (i=0;i<6;i++)
		{
			{
				for (j = 31; j >= 0; j--) {
					if ( BinaryStr[j] == lSerial[i])
					{
						c_id[n * 6 + i] = CryptStr[j];
						break;
					}
				}
			}
		}
		int shif = (n + 1) * 2;
		lSerialN = ( l_id[n] >> (32 - shif) ) & mask[n];
		lSerialN |= (l_id[n + 1] << shif);
	}
	c_id[26] = '\0';
	strcpy(serialNumber,c_id);

	free(c_id);
	free(lid);
	free(l_id);
	free(lSerial);
	//fprintf(fp,"%s", serialNumber);
	//fclose(fp);
}
 
就这么多了-。-,当初浪费了一个暑假时间研究了这个东东,虽然辛苦,不过学到了8少东西啊
enjoy
 
忘了,再补充一个function pointer的实际应用
6.function pointer的实际应用
这个概念也是我研究完服务器端才有的,我很喜欢这种方法,觉得节省了switch..case statement的空间了

代码:
//数据接收函数 ,得到指令index
BOOL CIOCP::Recv(CIOCP* lp_this, IOCP_IO_PTR lp_io, DWORD * dwBytes)
{
	BOOL bRet;
	BYTE *pData, *pBuffer, *Decrypt;
	int lengthLo, lengthHi, length, packetType;

	bRet = true;
	pData = (BYTE*)(lp_io->wsaBuf.buf);
	pBuffer = pData;
	lengthLo = *pBuffer & 0xff;
	pBuffer ++;
	lengthHi = *pBuffer & 0xff;
	pBuffer ++;
	length= lengthHi * 256 + lengthLo;	
	if (length != *dwBytes)	return false;
	length -= 2;
	Decrypt = (BYTE*)GlobalAlloc(GMEM_ZEROINIT, length);
	
	opt_memcpy((char*)Decrypt, (char*)pBuffer, length);

    length = decrypt((char*)Decrypt, length);
	packetType = *Decrypt & 0xff;
	//printf("packet %d\n",packetType);
	if (m_Func[packetType] == NULL) bRet = false;
	else
	(lp_this->*m_Func[packetType])(lp_io, Decrypt + 1);
	return bRet;
}
对照的从pointer列表里头取得需要调用的函数


bool (CIOCP::*m_Func[10])(IOCP_IO_PTR lp_io, BYTE* packet) = {
	&CIOCP::RequestAuthLogin,	//0
	NULL,						//1
	NULL,						//2
	NULL,						//3
	NULL,						//4
	NULL,						//5
	NULL,						//6
	&CIOCP::GGAuth				//7
};


bool CIOCP::RequestAuthLogin(IOCP_IO_PTR lp_io, BYTE* pData)
{
	char* pname, *ppass, *pBuffEx;
	BYTE *pBuffer, *pPwd;

	int length;

	//printf("RequestAuthLogin\n");
	pBuffer = pData;
	pname = (char*)pBuffer;
	pBuffer += 14;
	ppass = (char*)pBuffer;

	pBuffEx = lp_io->wsaBuf.buf;
	pPwd = (BYTE*)GlobalAlloc(GMEM_ZEROINIT, 16);	
	getPwd(ppass, pPwd, lstrlenA(ppass));
	
	CheckPassword();
	
	length = EnAssemble(pBuffEx, BUFFER_SIZE, "hcdddddddddddch", 50, 3, rand(), 65894300, 0, 0, 1002, 0,0,2, 14705248, 0,0,0,0);
	lp_io->wsaBuf.len = length;
	GlobalFree(pPwd);
	return TRUE;
}

bool CIOCP::GGAuth(IOCP_IO_PTR lp_io, BYTE* pData)
{	
	BYTE * pBuffer;
	int length;
	//printf("GameGuardAuth\n");
	
	length = sizeof(_GGAuth);
	pBuffer = (BYTE*)GlobalAlloc(GMEM_ZEROINIT, length);
	RtlCopyMemory(pBuffer, _GGAuth, length);
	checksum(pBuffer, length);
	encrypt((char*)(pBuffer + 2), length - 2);
	lp_io->wsaBuf.len = length;
	RtlCopyMemory(lp_io->wsaBuf.buf, pBuffer, length);
	GlobalFree(pBuffer);
	return TRUE;
}
 
好东西啊~~慢慢学
 
后退
顶部