最近我对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.安装部分

Code
1
#define UNICODE
2
#define _UNICODE
3
4
#include "stdafx.h"
5
#include <stdio.h>
6
#include <tchar.h>
7
#include <ws2spi.h>
8
#include <sporder.h>
9
#pragma comment(lib,"ws2_32.lib")
10
#pragma comment(lib,"sporder.lib");
11
12
GUID FilterGuid=
{0xfdfdfdfd,0x116a,0x5151,
{0x8f,0xd4,0x21,0x21,0xf2,0x7b,0xd9,0xa9}};
13
GUID FilterChainGuid=
{0xfdfdfdfd,0x2121,0x5151,
{0x8f,0xd4,0x21,0x21,0xcc,0x7b,0xd9,0xaa}};
14
WSAPROTOCOL_INFOW *lpAllProtoInfo;
15
int TotalNum;
16
17
void usage()
18

{
19
printf("***********************************\n");
20
printf("* Made by ffantasyYD *\n");
21
printf("* QQ:76889713 *\n");
22
printf("* email:ffantasyYD@163.com *\n");
23
printf("***********************************\n");
24
printf("This program have 1 param: InstallFilter (/install or /remove)\n");
25
printf("If you select '/install',you will install the filter;\n");
26
printf("and if you select '/remove',you will remove the filter that you have installed.\n");
27
}
28
29
void ChangeFromUnicode(unsigned short *UValue,char *Value)
30

{
31
int i=0;
32
33
while(UValue[i]!=0)
34
{
35
Value[i]=UValue[i];
36
i++;
37
}
38
Value[i]=0;
39
}
40
41
void ChangeToUnicode(char *Value,unsigned short *UValue)
42

{
43
int i=0;
44
45
while(Value[i]!=0)
46
{
47
UValue[i]=Value[i];
48
i++;
49
}
50
UValue[i]=0;
51
}
52
53
bool GetAllFilter()
54

{
55
DWORD size=0;
56
int Error;
57
lpAllProtoInfo=NULL;
58
TotalNum=0;
59
60
::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error);
61
if(Error!=WSAENOBUFS)
62
{
63
return 0;
64
}
65
66
lpAllProtoInfo=new WSAPROTOCOL_INFOW[size];
67
memset(lpAllProtoInfo,0,size);
68
if((TotalNum=::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error))==SOCKET_ERROR)
69
{
70
return 0;
71
}
72
return 1;
73
}
74
75
bool FreeFilter()
76

{
77
delete []lpAllProtoInfo;
78
return 1;
79
}
80
81
void install()
82

{
83
int i;
84
DWORD ThisId,NextId;
85
WSAPROTOCOL_INFOW ipProtoInfo,ipChainInfo;
86
char szProto[WSAPROTOCOL_LEN+1]=
{0};
87
char dllPath[MAX_PATH]=
{0};
88
unsigned short UDllPath[MAX_PATH]=
{0};
89
DWORD *lpCatalogEntry=NULL;
90
int iptrue=0,tcptrue=0;
91
92
//以下部分是安装自己的服务提供者
93
GetAllFilter();
94
for(i=0;i<TotalNum;i++)
95
{
96
if((iptrue!=1)&&(lpAllProtoInfo[i].iAddressFamily==AF_INET)&&(lpAllProtoInfo[i].iProtocol==IPPROTO_IP))
97
{
98
memcpy(&ipProtoInfo,&lpAllProtoInfo[i],sizeof(WSAPROTOCOL_INFOW));
99
ipProtoInfo.dwServiceFlags1=lpAllProtoInfo[i].dwServiceFlags1 & (~XP1_IFS_HANDLES);
100
iptrue++;
101
}
102
if((tcptrue!=1)&&(lpAllProtoInfo[i].iAddressFamily==AF_INET)&&(lpAllProtoInfo[i].iProtocol==IPPROTO_TCP))
103
{
104
memcpy(&ipChainInfo,&lpAllProtoInfo[i],sizeof(WSAPROTOCOL_INFOW));
105
NextId=lpAllProtoInfo[i].dwCatalogEntryId;
106
ipChainInfo.dwServiceFlags1=lpAllProtoInfo[i].dwServiceFlags1 & (~XP1_IFS_HANDLES);
107
tcptrue++;
108
}
109
}
110
111
strcpy(szProto,"IP_FILTER");
112
ChangeToUnicode(szProto,ipProtoInfo.szProtocol);
113
ipProtoInfo.ProtocolChain.ChainLen=0; //表示分层服务提供者
114
115
::GetCurrentDirectory(MAX_PATH,dllPath);
116
strcat(dllPath,"http://www.cnblogs.com/chenzhifei/admin/file://PacketFilter.dll/");
117
ChangeToUnicode(dllPath,UDllPath);
118
if(::WSCInstallProvider(&FilterGuid,UDllPath,&ipProtoInfo,1,NULL)==SOCKET_ERROR)
119
{
120
printf("Install Provider failed!\n");
121
return;
122
}
123
FreeFilter();
124
125
//以下部分是安装协议链
126
GetAllFilter();
127
for(i=0;i<TotalNum;i++)
128
{
129
if(lpAllProtoInfo[i].ProviderId==FilterGuid)
130
{
131
ThisId=lpAllProtoInfo[i].dwCatalogEntryId;
132
break;
133
}
134
}
135
memset(szProto,0,WSAPROTOCOL_LEN+1);
136
strcpy(szProto,"IP_CHAIN");
137
ChangeToUnicode(szProto,ipChainInfo.szProtocol);
138
139
if(ipChainInfo.ProtocolChain.ChainLen==1)
140
{
141
ipChainInfo.ProtocolChain.ChainEntries[1]=NextId;
142
}
143
else
144
{
145
for(i=ipChainInfo.ProtocolChain.ChainLen;i>0;i--)
146
{
147
ipChainInfo.ProtocolChain.ChainEntries[i]=ipChainInfo.ProtocolChain.ChainEntries[i-1];
148
}
149
}
150
ipChainInfo.ProtocolChain.ChainLen++;
151
ipChainInfo.ProtocolChain.ChainEntries[0]=ThisId; //将自定义的服务提供者放到协议链的顶端
152
153
if(::WSCInstallProvider(&FilterChainGuid,UDllPath,&ipChainInfo,1,NULL)==SOCKET_ERROR)
154
{
155
printf("Install Chain failed!\n");
156
return;
157
}
158
FreeFilter();
159
160
//以下部分是重排系统中的服务提供者
161
GetAllFilter();
162
lpCatalogEntry=new DWORD[TotalNum];
163
int k=0;
164
for(i=0;i<TotalNum;i++)
165
{
166
if((lpAllProtoInfo[i].ProviderId==FilterGuid)||(lpAllProtoInfo[i].ProviderId==FilterChainGuid))
167
{
168
lpCatalogEntry[k]=lpAllProtoInfo[i].dwCatalogEntryId;
169
k++;
170
}
171
}
172
for(i=0;i<TotalNum;i++)
173
{
174
if((lpAllProtoInfo[i].ProviderId!=FilterGuid)&&(lpAllProtoInfo[i].ProviderId!=FilterChainGuid))
175
{
176
lpCatalogEntry[k]=lpAllProtoInfo[i].dwCatalogEntryId;
177
k++;
178
}
179
}
180
181
if(::WSCWriteProviderOrder(lpCatalogEntry,TotalNum)!=ERROR_SUCCESS)
182
{
183
printf("Write the provider's order failed!\n");
184
return;
185
}
186
187
delete []lpCatalogEntry;
188
FreeFilter();
189
printf("Install successful!\n");
190
return;
191
}
192
193
void remove()
194

{
195
if(::WSCDeinstallProvider(&FilterChainGuid,NULL)==SOCKET_ERROR)
196
{
197
printf("Remove Chain failed!\n");
198
return;
199
}
200
if(::WSCDeinstallProvider(&FilterGuid,NULL)==SOCKET_ERROR)
201
{
202
printf("Remove Provider failed!\n");
203
return;
204
}
205
printf("Remove successful!\n");
206
return;
207
}
208
209
int main(int argc, char* argv[])
210

{
211
usage();
212
if(argc!=2)
213
{
214
return 0;
215
}
216
printf("Start


..\n");
217
218
if(strcmp(argv[1],"/install")==0)
219
{
220
install();
221
}
222
if(strcmp(argv[1],"/remove")==0)
223
{
224
remove();
225
}
226
return 0;
227
}
228
229
2.dll部分
230
#define UNICODE
231
#define _UNICODE
232
233
#include "stdafx.h"
234
#include "ws2spi.h"
235
#include "tchar.h"
236
#pragma comment (lib,"ws2_32.lib")
237
238
//extern "C" __declspec(dllexport) int WSPAPI WSPStartup(WORD wversionrequested,
239
// LPWSPDATA lpwspdata,
240
// LPWSAPROTOCOL_INFOW lpprotoinfo,
241
// WSPUPCALLTABLE upcalltable,
242
// LPWSPPROC_TABLE lpproctable);
243
GUID FilterGuid=
{0xfdfdfdfd,0x116a,0x5151,
{0x8f,0xd4,0x21,0x21,0xf2,0x7b,0xd9,0xa9}};
244
WSAPROTOCOL_INFOW *lpAllProtoInfo;
245
int TotalNum;
246
247
void ChangeFromUnicode(unsigned short *UFilterPath,char *FilterPath)
248

{
249
int i=0;
250
251
while(UFilterPath[i]!=0)
252
{
253
FilterPath[i]=UFilterPath[i];
254
i++;
255
}
256
FilterPath[i]=0;
257
}
258
259
bool GetAllFilter()
260

{
261
DWORD size;
262
int Error;
263
lpAllProtoInfo=NULL;
264
TotalNum=0;
265
266
::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error);
267
if(Error!=WSAENOBUFS)
268
{
269
return 0;
270
}
271
272
lpAllProtoInfo=new WSAPROTOCOL_INFOW[size];
273
memset(lpAllProtoInfo,0,size);
274
if((TotalNum=::WSCEnumProtocols(NULL,lpAllProtoInfo,&size,&Error))==SOCKET_ERROR)
275
{
276
return 0;
277
}
278
return 1;
279
}
280
281
bool FreeFilter()
282

{
283
delete []lpAllProtoInfo;
284
return 1;
285
}
286
287
int WSPAPI WSPSend(SOCKET s,
288
LPWSABUF lpBuffers,
289
DWORD dwBufferCount,
290
LPDWORD lpNumberOfBytesSent,
291
DWORD dwFlags,
292
LPWSAOVERLAPPED lpOverlapped,
293
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE,
294
LPWSATHREADID lpthreadid,
295
LPINT lperrno)
296

{
297
//不往下一层spi传递,以达到滤包的目的。如要对包进行筛选,也可以在此进行处理。
298
return 0;
299
}
300
301
int WSPAPI WSPSendTo(SOCKET s,
302
LPWSABUF lpbuffer,
303
DWORD dwbuffercount,
304
LPDWORD lpnumberofbytessent,
305
DWORD dwflags,
306
const struct sockaddr FAR *lpto,
307
int itolen,
308
LPWSAOVERLAPPED lpoverlapped,
309
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpcompletionroutine,
310
LPWSATHREADID lpthreadid,
311
LPINT lperrno)
312

{
313
//不往下一层spi传递,以达到滤包的目的。如要对包进行筛选,也可以在此进行处理。
314
return 0;
315
}
316
317
int WSPAPI WSPStartup(WORD wversionrequested,
318
LPWSPDATA lpwspdata,
319
LPWSAPROTOCOL_INFOW lpprotoinfo,
320
WSPUPCALLTABLE upcalltable,
321
LPWSPPROC_TABLE lpproctable)
322

{
323
DWORD ThisLayerId,NextLayerId;
324
HINSTANCE hNextDll;
325
LPWSPSTARTUP lpWspStartup;
326
int Error;
327
int i;
328
329
GetAllFilter();
330
331
for(i=0;i<TotalNum;i++)
332
{
333
if(memcmp(&lpAllProtoInfo[i].ProviderId,&FilterGuid,sizeof(GUID))==0)
334
{
335
ThisLayerId=(&lpAllProtoInfo[i])->dwCatalogEntryId;
336
break;
337
}
338
}
339
340
for(i=0;i<lpprotoinfo->ProtocolChain.ChainLen;i++)
341
{
342
if(lpprotoinfo->ProtocolChain.ChainEntries[i]==ThisLayerId)
343
{
344
NextLayerId=lpprotoinfo->ProtocolChain.ChainEntries[i+1];
345
break;
346
}
347
}
348
349
int DllPathSize=MAX_PATH;
350
unsigned short UnicodeDllPath[MAX_PATH]=
{0};
351
char DllPath[MAX_PATH]=
{0},ExpandDllPath[MAX_PATH]=
{0};
352
353
for(i=0;i<TotalNum;i++)
354
{
355
if(NextLayerId==lpAllProtoInfo[i].dwCatalogEntryId)
356
{
357
::WSCGetProviderPath(&lpAllProtoInfo[i].ProviderId,UnicodeDllPath,&DllPathSize,&Error);
358
}
359
}
360
361
ChangeFromUnicode(UnicodeDllPath,DllPath);
362
363
::ExpandEnvironmentStrings(DllPath,ExpandDllPath,MAX_PATH);
364
hNextDll=::LoadLibrary(ExpandDllPath); //加载下一层服务提供者
365
lpWspStartup=(LPWSPSTARTUP)::GetProcAddress(hNextDll,"WSPStartup");
366
367
lpWspStartup(wversionrequested,lpwspdata,lpprotoinfo,upcalltable,lpproctable);
368
369
lpproctable->lpWSPSendTo=WSPSendTo;
370
lpproctable->lpWSPSend=WSPSend;
371
372
FreeFilter();
373
return 0;
374
}
375
376
BOOL APIENTRY DllMain( HANDLE hModule,
377
DWORD ul_reason_for_call,
378
LPVOID lpReserved)
379

{
380
return TRUE;
381
}
382
383
文章写的粗浅,望大家见谅。如果大家有什么不明白,请参看TOo2y的《基于SPI的数据报过滤原理与实现》以及《windows网络编程技术》第14章 winsock 2 服务提供者接口。