• Welcome to ComeFromChina Community (CFC中文网)! We are the largest Chinese Canadian community forum in Ottawa. Please to participate in discussions, post topics, view images, and access full community features. Join us today! 欢迎来到CFC中文网。请登录以参与讨论、发布主题并查看图片。

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

苦逼热狗

路边通讯社社长
VIP
注册
2002-10-12
消息
47,086
荣誉分数
2,374
声望点数
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;
}
 
好东西啊~~慢慢学
 
后退
顶部
首页 论坛
消息
我的