绝大多数具有网络功能的软件都是基于socket(网络套接字)实现的,或者是使用了更高层的接口(例如:WinInet API)而最底层仍然是基于socket实现的。在大多数操作系统中都实现了socket接口,在WINDOWS操作系统中的实现称为WinSock。WinSock是以DLL的形式实现的,现在WinSock有两个版本的实现:WinSock 1.1(winsock.dll)和WinSock 2(ws2_32.dll),ws2_32.dll既支持WinSock 1.1的函数又支持WinSock 2规范中增加的许多额外的函数,我们可以像Win32 API一样的使用它,只是需要额外链接一个库而已。这里不讨论具体的WinSock编程,只是让大家了解,WinSock是WINDOWS应用程序与网络打交道的接口,是我们实现网络监控这个目的的突破口。
好了,那我们就开始吧!“网络监控”这个范围有点太泛了,我们先把范围缩小到监控网络连接请求这个具体的操作上面吧,这也就是我的作品:IPGate 网址过滤器 的核心技术。我们先来看看一个TCP/IP连接是如何建立的:
客户机端 服务器端
======== ========
监听套接字 连接套接字
========== ==========
socket() socket()
bind() bind()
listen()
connect()----->accept()------>创建连接套接字
send()----------------------->recv()
recv()<-----------------------send()
.
.
.
closesocket() closesocket() closesocket()
我们可以看出,是客户机端的connect()执行实际的连接请求动作,我们再来看看connect函数的参数:
int connect(
SOCKET s, // 指定对哪个套接字进行操作
const struct sockaddr FAR *name, // 这是一个描述服务器IP地址的结构
int namelen // 指明上面这个结构的大小
);
对于name参数,由于sockaddr结构内容依赖于具体的协议,所以对于TCP/IP协议,我们传递sockaddr_in这个结构,再来看看这个结构:
struct sockaddr_in{
short sin_family; // 必须为AF_INET
unsigned short sin_port; // IP端口号
struct in_addr sin_addr; // 标识IP地址的一个结构体
char sin_zero[8]; // 为了兼容sockaddr而设置的占位空间
};
到这儿,我们可以看出,对于一次连接请求的目的地信息,已经全部在传入的参数中描述清楚了,接下来要做的就设置一个全局API钩子,钩住所有程序的connect()调用,在进行实际的connect()操作之前,我们先分析传入的参数,如果发现连接目的地是我们不允许访问的,就不进行连接操作,仅返回一个错误码就可以了。就这么简单,就能实现一夫当关,万夫莫开的效果。
同样的道理,也可以Hook其它函数而实现监控整个网络通讯各方面的内容,比如说截取发送和接收的数据包进行分析等等,这就取决于设计者的意图了,大家不妨动手试试看,感受一下Hook API的魅力。