最近我对spi滤包技术(就是防火墙基于用户级的滤包)做了一番研究,也自己编程进行了实现,到现在,也算是有些心得了吧。因此,写出这篇算是总结也算是心得的东西拿出来和大家分享,希望对大家有用。在进入正题之前,我先要感谢那些无私共享出自己研究成果的前辈们,尤其是safechina的TOo2y,他的文章《基于SPI的数据报过滤原理与实现》可以说是我研究spi滤包技术的良师,说得不好听,我这个源代码实际上就是他那篇源代码的翻版。 说到spi滤包,首先要了解一下winsock2 spi。spi中文名叫服务提供者接口。winsock2 spi允许开发两种服务提供者:传输提供者和名字空间提供者,在这里我们要用到的是传输提供者。winsock2 spi与winsock2 api相对应,分别在winsock的两端。它们的具体结构如下图: ------------------------------------- |Windows socket 2 应用程序| -----------------------------------------Windows socket 2 API | WS2_32.DLL | ---------------------------------------- Windows socket 2 SPI | 分层提供者 | -------------------SPI | 分层提供者 | ----------------------------------- SPI | 基础提供者 | -----------------------------------
对于大部分winsock2 api函数,都对应一个winsock2 spi函数。调用这些winsock2 api函数时,ws2_32.dll会将其映射到一个winsock2 spi函数然后执行,以实现我们的正常通信(如WSASend映射到WSPSend)。而这些spi函数,在需要时以加载dll的形式进入内存。所以,每一个服务提供者就对应一个dll,在需要该服务提供者时,ws2_32.dll就会加载其对应的dll。但是,由上图可以看出来,传输服务提供者可以不止一个(事实上一个系统中的服务提供者都不会只有一个),允许存在多层的服务提供者,那么,我们在实现通信应用程序时,会加载哪一个服务提供者的dll呢?这就是ws2_32.dll的工作了。winsock 2有一个系统配置表,里面存放着系统中所有传输服务提供者的信息。在应用程序建立套接字时,ws2_32.dll在这个winsock 2系统配置表中按顺序搜索第一个与套接字相匹配的传输服务提供者,然后加载此提供者对应的dll。 以上就是应用服务提供者的基本原理了,但是我们现在是要滤包,究竟应该怎么去实现呢?其实,只要我们能自己构建一个服务提供者,然后把它放到所有服务提供者的顶端,那么,ws2_32.dll在需要时就会加载我们自己的服务提供者,就会加载我们自己打造的dll,那么,我们只要在我们自己的这个dll里面实现滤包就行了。但是,具体应该怎么做呢?还是让我们先回到上图吧。从图中可以看出,服务提供者可以是分层的。比如,我们现在加载的是第一层的服务提供者的dll,在这个dll中,有一个唯一的入口函数是WSPStartup,这个函数与api WSAStartup相对应,其函数原型如下: int WSPAPI WSPStartup(WORD wversionrequested, LPWSPDATA lpwspdata, LPWSAPROTOCOL_INFOW lpprotoinfo, WSPUPCALLTABLE upcalltable, LPWSPPROC_TABLE lpproctable); 前面说了,这个函数是传输服务提供者的唯一的入口函数,而其他的与api相对应的spi函数是由参数lpproctable给出的。lpproctable是一个指针,指向一个结构,而这个结构中就存放着另外那些spi函数的指针。好了,回到原题,假设我们现在加载第一层服务提供者的dll,那么,当应用程序调用api比如WSASend时,就会通过WSPStartup函数的lpproctable参数,寻找spi函数WSPSend然后执行。一般情况下,在这一层spi的WSPSend函数中,在执行其特定操作之后,就会调用下一层服务提供者的WSPSend函数,实现往下一层的传递(这要通过加载下一层spi的dll,找到它的WSPStartup函数来实现)。这就是正常的通信。现在,我们是要滤包,那么,只要我们在这一层的WSPSend函数中不调用下一层的WSPSend不就行了?正确!这就是我这个spi滤包的思路了。 下面给出源代码,包括安装部分和dll部分,欢迎大家批评。
1.安装部分 #define UNICODE #define _UNICODE
#include "stdafx.h" #include <stdio.h> #include <tchar.h> #include <ws2spi.h> #include <sporder.h> #pragma comment(lib,"ws2_32.lib") #pragma comment(lib,"sporder.lib");
GUID FilterGuid={0xfdfdfdfd,0x116a,0x5151,{0x8f,0xd4,0x21,0x21,0xf2,0x7b,0xd9,0xa9}}; GUID FilterChainGuid={0xfdfdfdfd,0x2121,0x5151,{0x8f,0xd4,0x21,0x21,0xcc,0x7b,0xd9,0xaa}}; WSAPROTOCOL_INFOW *lpAllProtoInfo; int TotalNum;
void usage() { printf("***********************************\n"); printf("* Made by ffantasyYD *\n"); printf("* QQ:76889713 *\n"); printf("* email:ffantasyYD@163.com *\n"); printf("***********************************\n"); printf("This program have 1 param: InstallFilter (/install or /remove)\n"); printf("If you select '/install',you will install the filter;\n"); printf("and if you select '/remove',you will remove the filter that you have installed.\n"); }
void ChangeFromUnicode(unsigned short *UValue,char *Value) { int i=0;
while(UValue[i]!=0) { Value[i]=UValue[i]; i++; } Value[i]=0; }
void ChangeToUnicode(char *Value,unsigned short *UValue) { int i=0;
while(Value[i]!=0) { UValue[i]=Value[i]; i++; } UValue[i]=0; }
bool GetAllFilter() { DWORD size=0; int Error; lpAllProtoInfo=NULL; TotalNum=0;
::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error); if(Error!=WSAENOBUFS) { return 0; }
lpAllProtoInfo=new WSAPROTOCOL_INFOW[size]; memset(lpAllProtoInfo,0,size); if((TotalNum=::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error))==SOCKET_ERROR) { return 0; } return 1; }
bool FreeFilter() { delete []lpAllProtoInfo; return 1; }
void install() { int i; DWORD ThisId,NextId; WSAPROTOCOL_INFOW ipProtoInfo,ipChainInfo; char szProto[WSAPROTOCOL_LEN+1]={0}; char dllPath[MAX_PATH]={0}; unsigned short UDllPath[MAX_PATH]={0}; DWORD *lpCatalogEntry=NULL; int iptrue=0,tcptrue=0;
//以下部分是安装自己的服务提供者 GetAllFilter(); for(i=0;i<TotalNum;i++) { if((iptrue!=1)&&(lpAllProtoInfo[i].iAddressFamily==AF_INET)&&(lpAllProtoInfo[i].iProtocol==IPPROTO_IP)) { memcpy(&ipProtoInfo,&lpAllProtoInfo[i],sizeof(WSAPROTOCOL_INFOW)); ipProtoInfo.dwServiceFlags1=lpAllProtoInfo[i].dwServiceFlags1 & (~XP1_IFS_HANDLES); iptrue++; } if((tcptrue!=1)&&(lpAllProtoInfo[i].iAddressFamily==AF_INET)&&(lpAllProtoInfo[i].iProtocol==IPPROTO_TCP)) { memcpy(&ipChainInfo,&lpAllProtoInfo[i],sizeof(WSAPROTOCOL_INFOW)); NextId=lpAllProtoInfo[i].dwCatalogEntryId; ipChainInfo.dwServiceFlags1=lpAllProtoInfo[i].dwServiceFlags1 & (~XP1_IFS_HANDLES); tcptrue++; } }
strcpy(szProto,"IP_FILTER"); ChangeToUnicode(szProto,ipProtoInfo.szProtocol); ipProtoInfo.ProtocolChain.ChainLen=0; //表示分层服务提供者
::GetCurrentDirectory(MAX_PATH,dllPath); strcat(dllPath,"\\PacketFilter.dll"); ChangeToUnicode(dllPath,UDllPath); if(::WSCInstallProvider(&FilterGuid,UDllPath,&ipProtoInfo,1,NULL)==SOCKET_ERROR) { printf("Install Provider failed!\n"); return; } FreeFilter();
//以下部分是安装协议链 GetAllFilter(); for(i=0;i<TotalNum;i++) { if(lpAllProtoInfo[i].ProviderId==FilterGuid) { ThisId=lpAllProtoInfo[i].dwCatalogEntryId; break; } } memset(szProto,0,WSAPROTOCOL_LEN+1); strcpy(szProto,"IP_CHAIN"); ChangeToUnicode(szProto,ipChainInfo.szProtocol);
if(ipChainInfo.ProtocolChain.ChainLen==1) { ipChainInfo.ProtocolChain.ChainEntries[1]=NextId; } else { for(i=ipChainInfo.ProtocolChain.ChainLen;i>0;i--) { ipChainInfo.ProtocolChain.ChainEntries[i]=ipChainInfo.ProtocolChain.ChainEntries[i-1]; } } ipChainInfo.ProtocolChain.ChainLen++; ipChainInfo.ProtocolChain.ChainEntries[0]=ThisId; //将自定义的服务提供者放到协议链的顶端
if(::WSCInstallProvider(&FilterChainGuid,UDllPath,&ipChainInfo,1,NULL)==SOCKET_ERROR) { printf("Install Chain failed!\n"); return; } FreeFilter();
//以下部分是重排系统中的服务提供者 GetAllFilter(); lpCatalogEntry=new DWORD[TotalNum]; int k=0; for(i=0;i<TotalNum;i++) { if((lpAllProtoInfo[i].ProviderId==FilterGuid)||(lpAllProtoInfo[i].ProviderId==FilterChainGuid)) { lpCatalogEntry[k]=lpAllProtoInfo[i].dwCatalogEntryId; k++; } } for(i=0;i<TotalNum;i++) { if((lpAllProtoInfo[i].ProviderId!=FilterGuid)&&(lpAllProtoInfo[i].ProviderId!=FilterChainGuid)) { lpCatalogEntry[k]=lpAllProtoInfo[i].dwCatalogEntryId; k++; } }
if(::WSCWriteProviderOrder(lpCatalogEntry,TotalNum)!=ERROR_SUCCESS) { printf("Write the provider's order failed!\n"); return; }
delete []lpCatalogEntry; FreeFilter(); printf("Install successful!\n"); return; }
void remove() { if(::WSCDeinstallProvider(&FilterChainGuid,NULL)==SOCKET_ERROR) { printf("Remove Chain failed!\n"); return; } if(::WSCDeinstallProvider(&FilterGuid,NULL)==SOCKET_ERROR) { printf("Remove Provider failed!\n"); return; } printf("Remove successful!\n"); return; }
int main(int argc, char* argv[]) { usage(); if(argc!=2) { return 0; } printf("Start..............\n"); if(strcmp(argv[1],"/install")==0) { install(); } if(strcmp(argv[1],"/remove")==0) { remove(); } return 0; }
2.dll部分 #define UNICODE #define _UNICODE
#include "stdafx.h" #include "ws2spi.h" #include "tchar.h" #pragma comment (lib,"ws2_32.lib")
//extern "C" __declspec(dllexport) int WSPAPI WSPStartup(WORD wversionrequested, // LPWSPDATA lpwspdata, // LPWSAPROTOCOL_INFOW lpprotoinfo, // WSPUPCALLTABLE upcalltable, // LPWSPPROC_TABLE lpproctable); GUID FilterGuid={0xfdfdfdfd,0x116a,0x5151,{0x8f,0xd4,0x21,0x21,0xf2,0x7b,0xd9,0xa9}}; WSAPROTOCOL_INFOW *lpAllProtoInfo; int TotalNum;
void ChangeFromUnicode(unsigned short *UFilterPath,char *FilterPath) { int i=0;
while(UFilterPath[i]!=0) { FilterPath[i]=UFilterPath[i]; i++; } FilterPath[i]=0; }
bool GetAllFilter() { DWORD size; int Error; lpAllProtoInfo=NULL; TotalNum=0;
::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error); if(Error!=WSAENOBUFS) { return 0; }
lpAllProtoInfo=new WSAPROTOCOL_INFOW[size]; memset(lpAllProtoInfo,0,size); if((TotalNum=::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error))==SOCKET_ERROR) { return 0; } return 1; }
bool FreeFilter() { delete []lpAllProtoInfo; return 1; }
int WSPAPI WSPSend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE, LPWSATHREADID lpthreadid, LPINT lperrno) { //不往下一层spi传递,以达到滤包的目的。如要对包进行筛选,也可以在此进行处理。 return 0; }
int WSPAPI WSPSendTo(SOCKET s, LPWSABUF lpbuffer, DWORD dwbuffercount, LPDWORD lpnumberofbytessent, DWORD dwflags, const struct sockaddr FAR *lpto, int itolen, LPWSAOVERLAPPED lpoverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpcompletionroutine, LPWSATHREADID lpthreadid, LPINT lperrno) { //不往下一层spi传递,以达到滤包的目的。如要对包进行筛选,也可以在此进行处理。 return 0; }
int WSPAPI WSPStartup(WORD wversionrequested, LPWSPDATA lpwspdata, LPWSAPROTOCOL_INFOW lpprotoinfo, WSPUPCALLTABLE upcalltable, LPWSPPROC_TABLE lpproctable) { DWORD ThisLayerId,NextLayerId; HINSTANCE hNextDll; LPWSPSTARTUP lpWspStartup; int Error; int i;
GetAllFilter();
for(i=0;i<TotalNum;i++) { if(memcmp(&lpAllProtoInfo[i].ProviderId,&FilterGuid,sizeof(GUID))==0) { ThisLayerId=(&lpAllProtoInfo[i])->dwCatalogEntryId; break; } }
for(i=0;i<lpprotoinfo->ProtocolChain.ChainLen;i++) { if(lpprotoinfo->ProtocolChain.ChainEntries[i]==ThisLayerId) { NextLayerId=lpprotoinfo->ProtocolChain.ChainEntries[i+1]; break; } }
int DllPathSize=MAX_PATH; unsigned short UnicodeDllPath[MAX_PATH]={0}; char DllPath[MAX_PATH]={0},ExpandDllPath[MAX_PATH]={0};
for(i=0;i<TotalNum;i++) { if(NextLayerId==lpAllProtoInfo[i].dwCatalogEntryId) { ::WSCGetProviderPath(&lpAllProtoInfo[i].ProviderId,UnicodeDllPath,&DllPathSize,&Error); } } ChangeFromUnicode(UnicodeDllPath,DllPath);
::ExpandEnvironmentStrings(DllPath,ExpandDllPath,MAX_PATH); hNextDll=::LoadLibrary(ExpandDllPath); //加载下一层服务提供者 lpWspStartup=(LPWSPSTARTUP)::GetProcAddress(hNextDll,"WSPStartup");
lpWspStartup(wversionrequested,lpwspdata,lpprotoinfo,upcalltable,lpproctable);
lpproctable->lpWSPSendTo=WSPSendTo; lpproctable->lpWSPSend=WSPSend;
FreeFilter(); return 0; }
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } 文章写的粗浅,望大家见谅。如果大家有什么不明白,请参看TOo2y的《基于SPI的数据报过滤原理与实现》以及《windows网络编程技术》第14章 winsock 2 服务提供者接口。
|