不要以为上网浏览网页很简单,其实不然。就是你上网打开网页这一简单的动作,就需要你先发送数据包给网站,它接收到了之后,根据你发连的数据包的IP地址,返回给你网页的数据包,也就是说,网页的浏览,实际上就是数据包的交换。所以,要保障网页信息安全,首先就需要保障数据包的安全。为此,我们设计了一种用Winsock2SPI技术加密数据包的方法,接下来就和大家简单的介绍一下。

一、Winsock2 SPI技术

Winsock2 SPI(Service Provider Interface)服务提供者接口建立在Windows开放系统架构WOSA(Windows Open System Architecture)之上,是Winsock系统组件提供的面向系统底层的编程接口。Winsock系统组件向上面向用户应用程序提供一个标准的API接口;向下在Winsock组件和Winsock服务提供者(比如TCP/IP协议栈)之间提供一个标准的SPI接口。各种服务提供者是Windows支持的DLL,挂靠在Winsock2 的Ws2_32.dll模块下。对用户应用程序使用的Winsock2 API中定义的许多内部函数来说,这些服务提供者都提供了它们的对应的运作方式(例如API函数WSAConnect有相应的SPI函数WSPConnect)。多数情况下,一个应用程序在调用Winsock2 API函数时,Ws2_32.dll会调用相应的Winsock2 SPI函数,利用特定的服务提供者执行所请求的服务。

Winsock2 SPI允许开发两类服务提供者——传输服务提供者和名字空间服务提供者。“传输提供者”(Transport Providers, 一般称作协议堆栈,例如TCP/IP)能够提供建立通信、传输数据、日常数据流控制和错误控制等传输功能方面的服务。“名字空间提供者”(Name Space Providers,例如DNS名字解析服务)则把一个网络协议的地址属性和一个或多个用户友好名称关联到一起,以便启用与应用无关的名字解析方案。

SPI的作用是负责连接核心层的驱动程序和高层的应用程序,系统产生的所有Winsock请求都由SPI函数完成。形象地说,就是当Winaock的数据包经过SPI层时,就会被SPI截获。因此可以通过利用SPI的这个特性,将截获的网络封包进行处理,从而完成所需要的信息加密等功能。

二、Winsok SPI在注册表中的位置

在win32操作系统中,Windows的传输服务提供者的结构信息都保存在注册表的HKEY - LOCAL - MACHINE \SYSTEM、CurrentControlSet、Services、WinSock2、Parametera\Protocol - Cata-log9 \CaUrlog - Entries分支下。在这个分支下,每个类型的服务提供者都会有一个子分支,子分支的PackedCatalogltem键保存的键值为传输服务提供者的路径、文件名及WSAPROTOCOLINFOW结构。

三、加密网络数据包如何用Winsock2SPI技术实现

1、安装SPI服务

SPI提供两种服务,基础服务提供者和分层服务提供者,这里我们只用到分层服务提供者,具体实现方法请参照源码中的InBtkp类。

要安装分层式服务提供者,需要建立两个WSAPROTO-IoLJNFOW目录条目结构。一个代表分层提供者(比如协议链长度等于O)。另一个代表一个协议链(比如,协议链长度大于1)。该协议链把分层提供者与一个基础提供者链接起来。应该用一个现成服务提供者的WSAPROTOTOL_INFOW目录条目结构的属性来初始化这两个结构。调用WSCEnrunProto-caLs函数,便可获得这个现成服务提供者的WSAPROTO-TOIJNFOW目录条目结构了。

利用WSClnsLaIIProvider安装SPI。函数实现如下:

WSClnstaIIPrOYider(&Pmv如rGuid,PROVIDER_pATH, &I—PLayeredlnfo,1.EmuCode); VVSCInstaIIPrmrider()是一个win-dows函数,其中的参数可以查阅MSND,这里不作多述。如果成功安装,会发表注册表:。HKEY。LOCAL - MACHINE\SYSTEM \CurrentControISet\Services \WinSock2 \Parameters \Proto-col - Catalog9 \Calalog- Entries分支下会多一项寸如果要御载
SPI,则利用函数CDeinstaIIProvider e LPGUID j lpProviderld,LPINT lpErrno)f该函数返回值为整型。

2、WSPStartup()函数的处理

WSPStartup函数是Winsoket应用程序调用SPI的初始化函数,在自己设计的服务提供者DLL模块中必须重新编写WSP-Startup函数的实现过程,并用Export导出,一以便Winsock应用程序调用。其中我们用WSPSend()和WSPRecv()两个函数为例,这两个函数相当于Winsock API中的Send()和Recv()。

WSPStartup()中对应代码表示为:

int WSPAPI WSPStartup(WORD wVersion, LPWSPDAT AlpWSPData, PWSAPROTOCOL_INFOWlpProtocollnfo, WSPUPCALLTABLE UpCaIITable, LPWSPPROC_TABLE ,lpProcTable)

{

//关键代码段;其它省略

ipProcTable ->lpWSPSend - WSPSend;

lpProcTable - >lpWSPRecv= WSPRecv;

这样,Winsock API WSASend()和WSARecv()就映射到WSPSend()和WSPRecv(),如果应用程序调用WSASend(),就会先调用YVSPSend(),调用WSAReeV()就会先调用WSPRecv()我们就能在WSPRecv()接收缓坤区中进行解密,同理,可以在WSPSend()发送前,对发送缓冲区加密,因此不必要每个WSASend()的接收缓冲区解密。

3、实现在WSASend()加密

在发送数据包前,对数据包加密,本文加密算法是把数据包中每一个(lpBuffers[i].buf[j])作加10操作。

关键代码如下:
int WSPAPI WSPSend (SOCKETs,LPWSABUF,lpBufters, D\NORDdwBufferCount, LPDWORD lpNumberOfBytesSent DWORD dwFlagst

LPWSAOVERLAPPEDlpOvsrlappad, LPWSAOVERLAPPED_COMPLETiONBOUTINE - IlpCompletionRoutin Errno)

LPWSATHREADIDlpThread'd, IPINT lpEr INT Ret;

SOCKjNFO*SocketContext;

LPWSAOVERLAPPED ProviderOverlapped;

int type;

SOCKADDRJN m.locaIBddr;

SOCK ADDRJN nUemote_addr;;

int namelen;

namelen=sizeof(SOCKADDR_IN),

//用getsockname得到套接宇的属性,从中可以得到

//IP地址,端口号,可以根据应用程序需要

//对特定IP或PORT接收到的包进行加密。

type =getsockname(s, (SOCKADDR8)&

mjocal3ddr,&namelen.)

type =getpeername(s, (SQCKADDR*)&

rruemote_addr,&namelen);

//加密,可以根据应用程序的要求,对特定IP和PORT

//进行加密,

inti.j;

//可以根据应用程序的要求用一条if语句判断发送包;

//的IP或PORT是否是要加密的包的标志

//根据getsockname()和getpeername()判断是否要

//入加密循环,,此处省略判断,对所有的数据包加密

这样,根据应用程序的要求.1以IP或PORT。为判断标志;’可以对发送的数据包进行加密。

4、实现在WSPRecv()中解密

得到接收的数据包,再对接收的数据包进行解密,本处解密算法是把每一个(lpBuflers[i].buf[j])的值减10,如果要用专用的解密算法只用在for循环中对(lpBuflers[i].buf[j])进行解密。

int WSPAPl WSPRecv( SOCKET s, LPWSABUF _lpBuffers, DWORD dwBufrerCount . LPDWORD

lpNumberOfBytesRecvd, LPDWORDlpFlags,

LPWSAOVERLAPPEDlpOverfapped,

LPWSAOVERLAPPED COMPLETION_ROUTINE-,

lpCompletionRoutine, . LPWSATHREADID lpTh readld,LPINT lpErrno) .

SOCKJNFO *SocketContext;

intRet

PWSAOVERLAPPED ProviderOverlapped; .:i: 'A,

Tf (MainUpCaIITable; lpWPUQuerySockcatHandleContcaxt

('si :(:LPDWORD) & SocketContext, lpErrno)*

SOCKET_ERROR)

return SOCKET_ERROR;

inttype;

SOCKADDRINmjocaIBddr;f v .. ,

SOC K ADDR_IN m remote addr; -i :

Int namelen;

namele3n : sizeof{SOC KADDRJN);

此处可以根据应用程序的要求;对特定的I或PORT进行解密,可以用ijf语句判断上面用getsockname()和gelpeenle()得到的I或PORT是不是应用程序要求解密的判断标志。这里是对所有的数据包解密。

这样,根据应用程序的要求,以IP或PORT为判断标志,可以对接收的数据包进行解密。

小知识之数据包

包(Packet)是TCP/IP协议通信传输中的数据单位,一般也称“数据包”。有人说,局域网中传输的不是“帧”(Frame)吗?没错,但是TCP/IP协议是工作在OSI模型第三层(网络层)、第四层(传输层)上的,而帧是工作在第二层(数据链路层)。上一层的内容由下一层的内容来传输,所以在局域网中,“包”是包含在“帧”里的。