如何使用WINSOCK Api hook拦截修改socket数据包

时间:2021-12-24 00:38:03

在Windows网络数据通讯层,通过封包技术在客户端挡截游戏服务器发送来的游戏控制数 据包,分析数据包并修改数据包;同时还可以按照游戏数据包结构创建数据包,再模拟客户端发送给游戏服务器,这个过程其实就是一个封包的过程。

封包的技术是实现第二类游戏外挂的最核心的技术。封包技术涉及的知识很广泛,实现方法也很多,如挡截 WinSock 、挡截 API 函数、挡截消息、 VxD 驱动程序等。在此我们也不可能在此文中将所有的封包技术都进行详细介绍,故选择两种在游戏外挂程序中最常用的两种方法:挡截 WinSock 和挡截 API 函数。

1 . 挡截WinSock

众所周知, Winsock 是 Windows 网络编程接口,它工作于 Windows 应用层,它提供与底层传输协议无关的高层数据传输编程接口。在 Windows 系统中,使用 WinSock 接口为应用程序提供基于 TCP/IP 协议的网络访问服务,这些服务是由 Wsock32.DLL 动态链接库提供的函数库来完成的。

由上说明可知,任何 Windows 基于 TCP/IP 的应用程序都必须通过 WinSock 接口访问网络,当然网络游戏程序也不例外。由此我们可以想象一下,如果我们可以控制 WinSock 接口的话,那么控制游戏客户端程序与服务器之间的数据包也将易如反掌。按着这个思路,下面的工作就是如何完成控制 WinSock 接口了。由上面的介绍可知, WinSock 接口其实是由一个动态链接库提供的一系列函数,由这些函数实现对网络的访问。有了这层的认识,问题就好办多了,我们可以制作一个类似的动态链接库来代替原 WinSock
接口库,在其中实现 WinSock32.dll 中实现的所有函数,并保证所有函数的参数个数和顺序、返回值类型都应与原库相同。在这个自制作的动态库中,可以对我们感兴趣的函数(如发送、接收等函数)进行挡截,放入外挂控制代码,最后还继续调用原 WinSock 库中提供的相应功能函数,这样就可以实现对网络数据包的挡截、修改和发送等封包功能。

下面重点介绍创建挡截 WinSock 外挂程序的基本步骤:

(1) 创建 DLL 项目,选择 Win32 Dynamic-Link Library ,再选择 An empty DLL project 。

(2) 新建文件 wsock32.h ,按如下步骤输入代码:

① 加入相关变量声明:

HMODULE hModule=NULL; // 模块句柄

char buffer[1000]; // 缓冲区

FARPROC proc; // 函数入口指针

② 定义指向原WinSock库中的所有函数地址的指针变量,因WinSock库共提供70多个函数,限于篇幅,在此就只选择几个常用的函数列出,有关这些库函数的说明可参考MSDN相关内容。

// 定义指向原 WinSock 库函数地址的指针变量。

SOCKET (__stdcall *socket1)(int ,int,int);// 创建 Sock 函数。

int   (__stdcall *WSAStartup1)(WORD,LPWSADATA);// 初始化 WinSock 库函数。

int   (__stdcall *WSACleanup1)();// 清除 WinSock 库函数。

int (__stdcall *recv1)(SOCKET ,char FAR * ,int ,int );// 接收数据函数。

int (__stdcall *send1)(SOCKET ,const char * ,int ,int);// 发送数据函数。

int (__stdcall *connect1)(SOCKET,const struct sockaddr *,int);// 创建连接函数。

int (__stdcall *bind1)(SOCKET ,const struct sockaddr *,int );// 绑定函数。

...... 其它函数地址指针的定义略。

(3) 新建 wsock32.cpp 文件,按如下步骤输入代码:

① 加入相关头文件声明:

#include

#include

#include "wsock32.h"

② 添加DllMain函数,在此函数中首先需要加载原WinSock库,并获取此库中所有函数的地址。代码如下:

BOOL WINAPI DllMain (HANDLE hInst,ULONG ul_reason_for_call,LPVOID lpReserved)

{

if(hModule==NULL){

// 加载原 WinSock 库,原 WinSock 库已复制为 wsock32.001 。

hModule=LoadLibrary("wsock32.001");

}

else return 1;

// 获取原 WinSock 库中的所有函数的地址并保存,下面仅列出部分代码。

if(hModule!=NULL){

// 获取原 WinSock 库初始化函数的地址,并保存到 WSAStartup1 中。

proc=GetProcAddress(hModule,"WSAStartup");

WSAStartup1=(int (_stdcall *)(WORD,LPWSADATA))proc;

// 获取原 WinSock 库消除函数的地址,并保存到 WSACleanup1 中。

proc=GetProcAddress(hModule i,"WSACleanup");

WSACleanup1=(int (_stdcall *)())proc;

// 获取原创建 Sock 函数的地址,并保存到 socket1 中。

proc=GetProcAddress(hModule,"socket");

socket1=(SOCKET (_stdcall *)(int ,int,int))proc;

// 获取原创建连接函数的地址,并保存到 connect1 中。

proc=GetProcAddress(hModule,"connect");

connect1=(int (_stdcall *)(SOCKET ,const struct sockaddr *,int ))proc;

// 获取原发送函数的地址,并保存到 send1 中。

proc=GetProcAddress(hModule,"send");

send1=(int (_stdcall *)(SOCKET ,const char * ,int ,int ))proc;

// 获取原接收函数的地址,并保存到 recv1 中。

proc=GetProcAddress(hModule,"recv");

recv1=(int (_stdcall *)(SOCKET ,char FAR * ,int ,int ))proc;

...... 其它获取函数地址代码略。

}

else return 0;

return 1;

}