分享
 
 
 

关于spi滤包技术的研究

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

最近我对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 服务提供者接口。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有