首页 > 技术精文 > C/C++ > 实战网页游戏外挂hook技术——攻城掠地(一)
2016
01-19

实战网页游戏外挂hook技术——攻城掠地(一)

在开始之前,还是简单介绍下hook技术吧,HOOK技术:在windows系统下编程,应该会接触到api函数的使用,常用的api函数大概有2000个左右。

HOOK存在的意义

最初有些人对某些api函数的功能不太满意,就产生了如何修改这些api,使之更好的服务于程序的想法,这样api hook就自然而然的出现了。我们可以通过api hook,改变一个系统api的原有功能。

windows自带的api有SetWindowsHookEx,那么这个函数使用虽然是非常不错,不过这家伙早已经被一般杀毒软件干死了啊,你要是用了这个函数360第一个报毒。所以我还是用本站讲的mook吧,隐藏的很深入,嘿嘿……

分析:

1.首先创建一个自己的socket,然后通过hook系统的send和recv函数,在send函数被调用的时候利用WSADuplicateSocket函数把游戏的socket共享给自己的socket,相当于你拿到了发包的权限,可以这么理解。然后之后就可以用我们自己的socket去调用send函数了,也就是发数据包了,这样不会掉线。否则直接掉线喽!这也是要hook的原因。

开始干吧,首先创建一个mfc工程,然后申明以下变量

WSAPROTOCOL_INFO LocalProtocolInfo;
_send g_trueSend = (_send)GetProcAddress(GetModuleHandleA("Ws2_32"),"send");
_recv g_trueRecv = (_recv)GetProcAddress(GetModuleHandleA("Ws2_32"),"recv");
SOCKET g_socket = NULL;
bool g_bIsInitWSA = false;
bool g_bIsDuplicated = false;

ghook_recv g_recv = NULL;

上面是一些准备工作,先把系统的send和recv 2个函数地址取到,然后通过socket描述创建一个我们自己的socket套接字,这样就可以用自己的socket发包啦,定义的类型如下

//////////////封包函数//////////////
typedef int (WINAPI *_send)(SOCKET s, const char *buf, int len, int flags);
int WINAPI hook_send(SOCKET s, const char *buf, int len, int flags);
typedef int (WINAPI *_recv)(SOCKET s, char *buf, int len, int flags);
int WINAPI hook_recv(SOCKET s, char *buf, int len, int flags);
//用来其它地方处理recv数据
typedef void (WINAPI *ghook_recv)(char *buf, int len);

接下来就是初始hook要用的变量,代码如下:

void InitHookApi()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2,2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if ( err != 0 ) {
		return ;
	}
	if (LOBYTE( wsaData.wVersion ) != 2 ||
		HIBYTE( wsaData.wVersion ) != 2 ) 
	{
		WSACleanup( );
		return ; 
	}

	//创建自己的socket
	g_socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,&LocalProtocolInfo,0,0);
	g_bIsInitWSA = true;
}

以上代码就是创建一个套接字,这个套接字属于我们自己,其目的就是为了克隆目标连接的套接字,后面套接字只用socket了,中文打的慢!

完成了这步之后,我们就要加载mk.dll,这个动态链接库封装了mhook中的2个方法,一个是t001,t002,名字随便取的。之前是为了不让别人知道是干嘛的,是不是很坏?看代码:

/////////////////hook api/////////////////////

BOOL StartHook()
{
	if (m_hook == NULL || m_unhook == NULL)
	{
		//实现HOOK函数
		hModule = LoadLibrary("mk.dll");
		if(hModule == NULL)
		{
			AfxMessageBox("模块加载失败");
			return FALSE;
		}
		m_hook = (t001)GetProcAddress(hModule,"t001");
		m_unhook = (t002)GetProcAddress(hModule,"t002");
		if (m_hook == NULL || m_unhook == NULL)
		{
			AfxMessageBox("error.");
			return FALSE;
		}
	}
	BOOL ret_send = m_hook((LPVOID*)&g_trueSend,hook_send);
	BOOL ret_recv = m_hook((LPVOID*)&g_trueRecv,hook_recv);
	return ret_send && ret_recv;
}

BOOL StopHook()
{
	if(m_unhook != NULL)
	{
		BOOL ret_send = m_unhook((LPVOID*)&g_trueSend);
		BOOL ret_recv = m_unhook((LPVOID*)&g_trueRecv);
		g_bIsDuplicated = false;//停止,重新复制socket
		g_bIsInitWSA = false;//需要重新创建socket
		return ret_send && ret_recv;
	}
	return FALSE;
}

这点对C++编程稍微熟点的人应该能看懂吧,t001就是hook函数,和windows的SetWindowsHookEx其实是一样的。另外一个就不用说了吧,看申明:

typedef BOOL (*t001)(PVOID *ppSystemFunction, PVOID pHookFunction);
typedef BOOL (*t002)(PVOID *ppHookedFunction);
t001 m_hook = NULL;
t002 m_unhook = NULL;
HMODULE hModule = NULL;

开始HOOK之后,我们还要处理send和recv函数,这个是系统自动调用的,因为它是系统函数嘛!

int WINAPI hook_send(SOCKET s, const char *buf, int len, int flags)
{
	if (!g_bIsDuplicated)
	{
		//为一个共享套接口创建一个新的描述字LocalProtocolInfo,自己的套接口
		if(buf != NULL && len >= 3 && (buf[3]&0xFF) == 0x24)//0x24点击主城返回的数据包
		{
			//83是socket信息,非HTTP
			int wRet = WSADuplicateSocket(s,GetCurrentProcessId(),&LocalProtocolInfo);
			if(wRet == 0)
			{
				//若无错误发生,WSADuplicateSocket()返回0
				//可以封包了
				g_bIsDuplicated = true;
				AfxGetApp()->GetMainWnd()->GetDlgItem(IDC_BTN_DATA_DEBUG)->EnableWindow();
			}
		}
	}
	return g_trueSend(s,buf,len,flags);
}

先说send,变量g_bIsDuplicated是为了判断是否已经为一个共享套接口创建一个新的描述字,如果已经创建就不需要再创建。创建目的是为了把游戏的socket共享给我们前面创建的socket。我在第二个if判断了数据库长度大于3并且第四个字节是0x24,之所以要判断是为了确定已经进入游戏了,不然可能就Hook错了目标,因为你打开游戏的时候可能会产生其它的http信息,比如说广告,游戏首页的HTTP请求。

处理完了send函数再看下recv函数,这个函数基本上不用处理,你只需要把数据解密拿来分析。

int WINAPI hook_recv(SOCKET s, char *buf, int len, int flags)
{
	int ret = g_trueRecv(s,buf,len,flags);
	//调用系统recv 让游戏自己处理; 
	return ret;
}

以上步骤才算是第一步,因为还要处理接收的数据,大部分游戏肯定会加密,我们还要破解加密算法才能正确处理接收到的数据。后面再讲处理数据包的流程,本文至此结束!

最后编辑:
作者:小企鹅
这个作者貌似有点懒,什么都没有留下。
捐 赠如果您觉得这篇文章有用处,请支持作者!鼓励作者写出更好更多的文章!

留下一个回复