GhostCHD网络封包引擎用户层过LaTale台服nProtect

这篇文章其实不能给外挂初学者带来什么帮助,因为我的目的不是调试NP保护下的游戏来找call什么的——事实上,在国服待的几年我已经把这游戏研究透了,不夸张地讲给我一年的全日制时间我能自己逆向写一份这游戏。

GhostCHD引擎只有一个功能——分析游戏的通信协议、然后自己发包。辅助软件坚决不读写游戏内存,即便是当年发布的GhostCHD辅助实际上也是一个小型的独立游戏客户端,而不依赖游戏本身。说白了就是脱机挂。脱机版本的GhostCHD我没有发布过,不过当年接代练生活技能就用的是脱机的,一天能同时接五六个单子,最后受不了的是宽带带宽的说。

这使得我的思路受NP的限制不大。众所周知NP主要防的是外部程序对游戏内存的读写。

在国服,我的思路的Hook部分需要做3个地方:发包的地方、收包的地方、以及游戏主循环中的一个地方。

由于NP据说会对游戏代码段进行校验,曾经有位大神曰过,解决代码段校验的最好办法就是不对游戏代码段进行修改,虽然我听了很恼火,但是想来想去没有更性价比的方法,所以原来的Hook需要改一改。

那么很自然地想到改为ws2_32.dll中的send与recv。这样比以前麻烦一些,主要是send一次有可能发两个以上的包,而recv接收的单位不一定是一个封包而是半个封包,需要把它看作字节流来分析。另外需要自己处理加密与解密——这个倒简单了,标准端不像国服用SDDynDll做动态加密,而是个很简单的xor加密,10年还是11年改过一次算法,不过还是很快被爱好者们贴到ragezone论坛上去了,由于早知道发包相关代码的位置,自己静态分析也非常方便。

至于游戏主循环的那个hook,接触过D3D编程的都知道D3D游戏的窗口循环调用的是PeekMessage。OK,hook到这儿。

因为不知道NP会不会对send和recv这么重要的函数做inline hook检查,所以我决定使用修改IAT的办法做Hook。IAT的具体函数指针的地址可以通过运行不加载NP的情况(如不加参数的启动,会报错)挂载OD调试来找出来。

在计算机程序战争中,永远只有一个公理:谁先来谁是爷。所以注入游戏没有什么防NP的技术——我们只需要在NP加载前把代码注入了就是了。

又有传言NP会扫描程序加载的模块,看有没有可疑的DLL。这个好办,我毕业设计就是干这个的,专门把DLL变成shell code来注入。

台服的客户端加了壳,所以不能注入后就去修改东西,要等壳加载完毕后再修改。最后Hook方面主要的代码就是这个样子:

#include <windows.h>

#pragma comment(lib, "ws2_32.lib")

void Encrypt(unsigned char *data, unsigned int len)
{
	const char *key = "qmfaktnpgjs";
	for (unsigned int i = 0; i < len; ++i)
	{
		if (data[i] != 0)
			data[i] ^= key[i % 11];
	}
}

int PASCAL Hook_send(SOCKET s, const char *buf, int len, int flags)
{
	//some code
}

int PASCAL Hook_recv(SOCKET s, char FAR * buf, int len, int flags)
{
	//some code
}

BOOL WINAPI Hook_PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
{
	//some code
	return PeekMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
}


extern "C" __declspec(dllexport) void main()
{
	DWORD* iat_send = (DWORD*)0x98A4AC;
	DWORD* iat_recv = (DWORD*)0x98A4E4;
	DWORD* iat_PeekMessageA = (DWORD*)0x98A410;
	DWORD pfn_send = (DWORD)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "send");
	DWORD pfn_recv = (DWORD)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "recv");
	DWORD pfn_PeekMessageA = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "PeekMessageA");

	while (!(*iat_send == pfn_send && *iat_recv == pfn_recv && *iat_PeekMessageA == pfn_PeekMessageA))
	{
		Sleep(10);
	}
	*iat_send = (DWORD)&Hook_send;
	*iat_recv = (DWORD)&Hook_recv;
	*iat_PeekMessageA = (DWORD)&Hook_PeekMessageA;
}

 

鉴于当年经常被人偷学技术,所以删掉了与挂有关的代码。仅供技术探讨。

3 Replies to “GhostCHD网络封包引擎用户层过LaTale台服nProtect”

    1. 热烈欢迎原住岛民加入程序员大家庭 哈哈~
      邮箱给力!偶也做一个这样的~

Leave a Reply

Your email address will not be published. Required fields are marked *