Форум программистов, компьютерный форум, киберфорум
Программирование драйверов
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.68/25: Рейтинг темы: голосов - 25, средняя оценка - 4.68
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
1

Сетевая фильтрация url и ip адресов в wfp

03.11.2014, 22:13. Просмотров 4469. Ответов 22
Метки нет (Все метки)

Доброго времени суток!

Решил изучить технологию WFP (Windows Filtering Platform) и написать драйвер режима ядра для фильтрации url-адресов и ip-адресов с портами.

Что нужно конкретно:
  • Фильтрация url-адресов по get запросу и возможность блокировать их, выдавая пользователю причину и информацию (например, страница заражена и тп)
  • Фильтрация и тот же функционал, что выше, но для ip-адресов и портов (например 93.88.100.15:1001). Пока что ipv4
  • Поиск по содержимому пакета и сравнение с шаблонами базы (например, заблокированных сайтов из файла hosts)
  • Взаимодействие с сервисом или модулем (программой) в юзермоде

Собственно, прочитав советы товарища Убежденный, то понял, что для версий <=ХР нужно юзать TDI,NDIS или LSA, но так как я ориентируюсь писать как минимум, =>Win 7, то нашел технологию WFP.

Почитав msdn, я немного прояснил структуру этого WFP, но, я не понял ничего про то, КАК реализовать драйвер и из каких библиотек\заголовочных файлов брать нужные функции.

В частности, пока мне не понятно вот этот момент :
достаточно установить фильтр в
правильное место сетевого стека и передавать в юзермодное приложение все,
что фильтр там нахватает. Ну а приложение может обрабатывать полученный
трафик как угодно, не мешая нормальной работе браузеров и других программ.
Помогите пожалуйста, примерами кода (пускай даже не полного), КАК использовать драйвер наглядно, и годной литературой (из литературы я активно изучаю:
Кликните здесь для просмотра всего текста
Руссинович, Соломон "Внутреннее устройство Windows 2000";
Свен Шрайбер "Недокументированные возможности Windows 2000";
Дж.Рихтер "Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows"; http://www.ozon.ru/context/detail/id/116668/
Walter Oney "Programming the microsoft windows driver model"; http://www.ozon.ru/context/detail/id/3137491/
Солдатов "Программирование драйверов Windows"; http://www.ozon.ru/context/detail/id/28515104/
"Даниель А. Нортон. Написание драйверов для Windows"; http://www.ozon.ru/context/detail/id/28515104/
P.Orwick, G.Smith "Developing Drivers with the Windows Driver Foundation"; http://www.ozon.ru/context/detail/id/3963383/ live
ISBN 978-5-9775-0185-9, 978-5-7502-0364-2, 978-0-7356-2374-3; 2008 г.
О.Зайцев "Rootkits, SpyWare/AdWare, Keyloggers & BackDoors. Обнаружение и защита (+ CD-ROM)"; http://www.ozon.ru/context/detail/id/2811431/
)

Варианты вот такого =>
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Add filter to block traffic on IP V4 for all applications. 
//
FWPM_FILTER0      fwpFilter;
FWPM_SUBLAYER0    fwpFilterSubLayer;  
 
RtlZeroMemory(&fwpFilter, sizeof(FWPM_FILTER0));
 
fwpFilter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
fwpFilter.action.type = FWP_ACTION_BLOCK;
 
if (&fwpFilterSubLayer.subLayerKey != NULL)
    fwpFilter.subLayerKey = fwpFilterSubLayer.subLayerKey;
 
fwpFilter.weight.type = FWP_EMPTY; // auto-weight.
fwpFilter.numFilterConditions = 0; // this applies to all application traffic
fwpFilter.displayData.name = L"Receive/Accept Layer Block";
fwpFilter.displayData.description = L"Filter to block all inbound connections.";
       
printf("Adding filter to block all inbound connections.\n");
result = FwpmFilterAdd0(engineHandle, &fwpFilter, NULL, NULL);
        
if (result != ERROR_SUCCESS)
    printf("FwpmFilterAdd0 failed. Return value: %d.\n", result);
else
    printf("Filter added successfully.\n");
для меня мало очевидны в виду того, что тут только кусок кода, а я не могу понять целостной картины.
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
03.11.2014, 22:13
Ответы с готовыми решениями:

Фильтрация и сравнение IP wia WFP
Доброго времени суток! Благодаря товарищу Убежденный, разобрался немного в теме и наваял код....

Фильтрация URL адресов в Cisco 2911
Есть access-list. Нужно ограничить доступ в социальные сети определенным ip адресам данного ACL....

Сколько можно добавить URL адресов в свою созданную Группу URl?
Добрый день. Подскажите пожалуйста, сколько можно добавить URL адресов в свою созданную Группу...

Сетевая фильтрация
Есть электромагнитная катушка от бытового прибора. Потребляемая мощность прибора 15 ватт. Прибор...

22
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
03.11.2014, 22:16  [ТС] 2
Вот то, что я нашел более менее вменяемое и наглядное:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
#include "stdafx.h"
 
#include <windows.h>
 
#include <fwpmu.h>
 
#include <stdio.h>
 
#include <conio.h>
 
#include "winreg.h"
 
#define bufSize 100
 
#pragma comment(lib, "fwpuclnt.lib")
 
#pragma region Прототипы
 
void AddAllFilters(GUID subLayerGUID, HANDLE engineHandle);
 
UINT64 AddFilter(GUID subLayerGUID, HANDLE engineHandle, UINT32 cond, char * str);
 
void AddFilter(GUID subLayerGUID, HANDLE engineHandle, GUID guid);
 
void DelFilter(HANDLE engineHandle);
 
void DelFilter(HANDLE engineHandle, GUID ID);
 
void DelSubLayer(HANDLE engineHandle, GUID _idSubLayer);
 
void CloseSeans(HANDLE engineHandle);
 
void AutoStart();
 
#pragma endregion
 
void _tmain(int argc, char* argv[])
 
{
 
DWORD result;//Результат произведенной операции. В случае успеха Error_Success
 
HANDLE engineHandle = NULL;//Основной двигатель процесса
 
#pragma region Открытие сеанса
 
result = FwpmEngineOpen0(
 
NULL,
 
RPC_C_AUTHN_WINNT,
 
NULL,
 
NULL,
 
&engineHandle);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("Engine Open failed with error: %d\n",result);
 
system("pause");
 
return;
 
}
 
printf("Engine Opened successfully!\n");
 
#pragma endregion
 
#pragma region Добавление подуровня
 
//GUID {6AE8A860-A30E-4253-80A1-C1807E17AAFC}
 
GUID subLayerGUID = { 0x6ae8a860, 0xa30e, 0x4253, { 0x80, 0xa1, 0xc1, 0x80, 0x7e, 0x17, 0xaa, 0xfc } };
 
FWPM_SUBLAYER0 *mySubLayer={0}; //Подуровень, в который будем прописывать наши фильтры
 
//Проверка на существование подуровня с данным GUID
 
result = FwpmSubLayerGetByKey0(engineHandle,&subLayerGUID,&mySubLayer);
 
if(result!=ERROR_SUCCESS)
 
{
 
mySubLayer=new FWPM_SUBLAYER0();
 
mySubLayer->subLayerKey = subLayerGUID;
 
mySubLayer->flags = FWPM_SUBLAYER_FLAG_PERSISTENT;
 
mySubLayer->weight=0x01;
 
mySubLayer->displayData.description=L"Sublayer";
 
mySubLayer->displayData.name=L"Sublayer";
 
result = FwpmSubLayerAdd0(engineHandle,mySubLayer,NULL);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("SubLayer Added failed with error: %d\n",result);
 
CloseSeans(engineHandle);
 
return;
 
}
 
printf("SubLayer Added successfully!\n");
 
}
 
else
 
printf("SubLayer is already exist.\n");
 
#pragma endregion
 
#pragma region Добавление фильтров
 
if(!(argc>1))//Консоль открывается из автозапуска
 
{
 
//Добавляем новые основные фильтры
 
AddAllFilters(subLayerGUID,engineHandle);
 
printf("Filters added successfully!\n");
 
}
 
else//Консоль открывается из GUI-оболочки
 
{
 
AutoStart();
 
//GUID {1C046A9A-9187-4629-8184-24836DFE5EEB}
 
GUID allFilterGUID = { 0x1c046a9a, 0x9187, 0x4629, { 0x81, 0x84, 0x24, 0x83, 0x6d, 0xfe, 0x5e, 0xeb } };
 
//Добавляем вспомогательный фильтр
 
AddFilter(subLayerGUID, engineHandle,allFilterGUID);
 
//Удаляем основные фильтры
 
DelFilter(engineHandle);
 
//Добавляем новые основные фильтры
 
AddAllFilters(subLayerGUID,engineHandle);
 
printf("Filters added successfully!\n");
 
//Удаляем вспомогательный фильтр
 
DelFilter(engineHandle,allFilterGUID);
 
}
 
#pragma endregion
 
CloseSeans(engineHandle);
 
return;
 
}
 
//Добавление массива фильтров
 
void AddAllFilters(GUID subLayerGUID, HANDLE engineHandle)
 
{
 
DWORD result = NULL;
 
#pragma region Подсчет каждого вида условий
 
char *buf = new char[bufSize];//Буфер для хранения считанной строки
 
int ipCount=0,//Количество условий по IP
 
portCount=0,//Количество условий по порту
 
appCount=0;//Количество условий по приложениям
 
//Считываем из БД
 
FILE *f = fopen("C:\\Program Files\\Filter\\BlockList.iff","r");
 
if(!f)
 
{
 
printf("File \"BlockList.iff\" dosen't exist!\n");
 
return;
 
}
 
//Считываем первую строку - блокировать весь трафик или нет
 
fgets(buf,bufSize,f);
 
buf[strlen(buf)-1]='\0';
 
FILE *filterFile = fopen("C:\\Program Files\\Filter\\FilterList.iff","w");
 
if(!filterFile)
 
{
 
printf("File \"FilterList.iff\" dosen't create!\n");
 
return;
 
}
 
UINT64 filterID=NULL;
 
printf("Blocked all traffik: %s.\n",buf);
 
if(!strcmp(buf,"true")) //Если нужно заблокировать весь трафик
 
{
 
filterID = AddFilter(subLayerGUID,engineHandle,0,"All");
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
fclose(filterFile);
 
fclose(f);
 
return;
 
}
 
//Считываем пустую строку
 
fgets(buf,bufSize,f);
 
int n=1;//Счетчик пустых строк
 
//Считывание количества каждого вида условий
 
while(!feof(f))
 
{
 
fgets(buf,bufSize,f);
 
//Если последняя строка, то выходим из цикла
 
if(feof(f))
 
break;
 
buf[strlen(buf)-1]='\0';
 
if(buf[0]=='\0')
 
{
 
n++;
 
continue;
 
}
 
switch (n)
 
{
 
case 1: ipCount++; break;
 
case 2: portCount++; break;
 
case 3: appCount++; break;
 
default: break;
 
}
 
}
 
printf("\nIP %d\nPort %d\nApplications %d\n\n",ipCount,portCount,appCount);
 
if(ipCount+portCount+appCount==0)
 
{
 
printf("There is no conditions.\n");
 
return;
 
}
 
#pragma endregion
 
#pragma region Создание фильтров
 
fseek(f,0,SEEK_SET);
 
fgets(buf,bufSize,f);//Пропускаем строку, содержащую значение флага, показывающего блокировать всё или нет
 
fgets(buf,bufSize,f);//Пропускаем первую пустую строку
 
int i;//счетчик циклов
 
UINT32 buffer=0;//буфер для считывания
 
//Блокировка по IP
 
for(i=0; i < ipCount; i++)
 
{
 
fscanf(f,"%d",&buffer);//cчитываем i-ый адрес
 
filterID = AddFilter(subLayerGUID,engineHandle,buffer,"IP");
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fgets(buf,bufSize,f);//Считываем пустую строку
 
//Блокировка по порту
 
for(i=0; i < portCount; i++)
 
{
 
fscanf(f,"%d",&buffer);//cчитываем i-ый порт БД
 
filterID = AddFilter(subLayerGUID,engineHandle,buffer,"Port");
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fgets(buf,bufSize,f);//Считываем пустую строку
 
//Блокировка по приложениям
 
for(i=0; i < appCount; i++)
 
{
 
fgets(buf,bufSize,f);//cчитываем i-ое приложение БД
 
buf[strlen(buf)-1]='\0';
 
filterID = AddFilter(subLayerGUID,engineHandle,0,buf);
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fclose(filterFile);
 
fclose(f);
 
#pragma endregion
 
}
 
//Добавление фильтра на подуровень
 
UINT64 AddFilter(GUID subLayerGUID, HANDLE engineHandle, UINT32 cond, char * str)
 
{
 
DWORD result = NULL;
 
#pragma region Создание фильтра
 
FWPM_FILTER0_ myFilter={0};
 
UINT64 id=NULL;
 
RtlZeroMemory(&myFilter,sizeof(myFilter));
 
FWPM_FILTER_CONDITION0 condition;
 
myFilter.action.type= FWP_ACTION_BLOCK;
 
myFilter.subLayerKey = subLayerGUID;
 
myFilter.weight.type = FWP_EMPTY;
 
myFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
 
myFilter.displayData.description=L"Filter";
 
myFilter.displayData.name=L"Filter";
 
if(str=="All")
 
{
 
myFilter.numFilterConditions = 0;
 
printf("Added all blocked filter.\n");
 
}
 
else
 
{
 
myFilter.numFilterConditions = 1;
 
myFilter.filterCondition = &condition;
 
if(str=="IP")
 
{
 
FWP_V4_ADDR_AND_MASK addr;//адрес, который нужно добавить в фильтр
 
addr.addr=cond;
 
addr.mask=0xffffffff;
 
printf("Added IP: %d\n",addr.addr);
 
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_V4_ADDR_MASK;
 
condition.conditionValue.v4AddrMask = &addr;
 
}
 
else
 
{
 
if(str=="Port")
 
{
 
UINT16 port = cond;//порт, который нужно добавить в фильтр
 
printf("Added port: %d\n",port);
 
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_UINT16;
 
condition.conditionValue.uint16=port;
 
}
 
else
 
{
 
wchar_t *app = new wchar_t[bufSize];
 
swprintf(app,L"%S",str);//приложение, которое нужно добавить в фильтр
 
FWP_BYTE_BLOB *applicationID = NULL;
 
result = FwpmGetAppIdFromFileName0(app,&applicationID);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("Add application \"%s\" failed with error: %d.\n\n",str,result);
 
return -1;
 
}
 
printf("Added application: %s\n",str);
 
condition.fieldKey = FWPM_CONDITION_ALE_APP_ID;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_BYTE_BLOB_TYPE;
 
condition.conditionValue.byteBlob=applicationID;
 
}
 
}
 
}
 
#pragma endregion
 
#pragma region Добавление фильтра
 
result = FwpmFilterAdd0(engineHandle,&myFilter,NULL,&id);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("Filter Added failed with error: %d\n\n",result);
 
return -1;
 
}
 
#pragma endregion
 
return id;
 
}
 
//Добавление вспомогательного фильтра на подуровень
 
void AddFilter(GUID subLayerGUID, HANDLE engineHandle, GUID guid)
 
{
 
DWORD result = NULL;
 
#pragma region Создание фильтра
 
FWPM_FILTER0_ myFilter={0};
 
RtlZeroMemory(&myFilter,sizeof(myFilter));
 
myFilter.action.type= FWP_ACTION_BLOCK;
 
myFilter.subLayerKey = subLayerGUID;
 
myFilter.weight.type = FWP_EMPTY;
 
myFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
 
myFilter.displayData.description = L"Secondary filter";
 
myFilter.displayData.name = L"Secondary filter";
 
myFilter.filterKey = guid;
 
myFilter.numFilterConditions = 0;
 
#pragma endregion
 
#pragma region Добавление фильтра
 
result = FwpmFilterAdd0(engineHandle,&myFilter,NULL,NULL);
 
if(result != ERROR_SUCCESS)
 
printf("Secondary filter added failed with error: %d\n\n",result);
 
else
 
printf("Secondary filter added successfully!\n");
 
#pragma endregion
 
}
 
//Удаление фильтров по сохраненным ID
 
void DelFilter(HANDLE engineHandle)
 
{
 
#pragma region Удаление фильтров по сохраненным ID
 
FILE *filterFile = fopen("C:\\Program Files\\Filter\\FilterList.iff","r");
 
if(!filterFile)
 
{
 
printf("File \"FilterList.iff\" dosen't exist!\n");
 
return;
 
}
 
UINT64 id = NULL;
 
DWORD result = NULL;
 
while(!feof(filterFile))
 
{
 
fscanf(filterFile,"%d",&id);//cчитываем i-ый адрес
0
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
03.11.2014, 22:18  [ТС] 3
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
filterID = AddFilter(subLayerGUID,engineHandle,buffer,"IP");
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fgets(buf,bufSize,f);//Считываем пустую строку
 
//Блокировка по порту
 
for(i=0; i < portCount; i++)
 
{
 
fscanf(f,"%d",&buffer);//cчитываем i-ый порт БД
 
filterID = AddFilter(subLayerGUID,engineHandle,buffer,"Port");
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fgets(buf,bufSize,f);//Считываем пустую строку
 
//Блокировка по приложениям
 
for(i=0; i < appCount; i++)
 
{
 
fgets(buf,bufSize,f);//cчитываем i-ое приложение БД
 
buf[strlen(buf)-1]='\0';
 
filterID = AddFilter(subLayerGUID,engineHandle,0,buf);
 
if(filterID!=-1)
 
fprintf(filterFile,"%d\n",filterID);
 
}
 
fclose(filterFile);
 
fclose(f);
 
#pragma endregion
 
}
 
//Добавление фильтра на подуровень
 
UINT64 AddFilter(GUID subLayerGUID, HANDLE engineHandle, UINT32 cond, char * str)
 
{
 
DWORD result = NULL;
 
#pragma region Создание фильтра
 
FWPM_FILTER0_ myFilter={0};
 
UINT64 id=NULL;
 
RtlZeroMemory(&myFilter,sizeof(myFilter));
 
FWPM_FILTER_CONDITION0 condition;
 
myFilter.action.type= FWP_ACTION_BLOCK;
 
myFilter.subLayerKey = subLayerGUID;
 
myFilter.weight.type = FWP_EMPTY;
 
myFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
 
myFilter.displayData.description=L"Filter";
 
myFilter.displayData.name=L"Filter";
 
if(str=="All")
 
{
 
myFilter.numFilterConditions = 0;
 
printf("Added all blocked filter.\n");
 
}
 
else
 
{
 
myFilter.numFilterConditions = 1;
 
myFilter.filterCondition = &condition;
 
if(str=="IP")
 
{
 
FWP_V4_ADDR_AND_MASK addr;//адрес, который нужно добавить в фильтр
 
addr.addr=cond;
 
addr.mask=0xffffffff;
 
printf("Added IP: %d\n",addr.addr);
 
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_V4_ADDR_MASK;
 
condition.conditionValue.v4AddrMask = &addr;
 
}
 
else
 
{
 
if(str=="Port")
 
{
 
UINT16 port = cond;//порт, который нужно добавить в фильтр
 
printf("Added port: %d\n",port);
 
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_UINT16;
 
condition.conditionValue.uint16=port;
 
}
 
else
 
{
 
wchar_t *app = new wchar_t[bufSize];
 
swprintf(app,L"%S",str);//приложение, которое нужно добавить в фильтр
 
FWP_BYTE_BLOB *applicationID = NULL;
 
result = FwpmGetAppIdFromFileName0(app,&applicationID);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("Add application \"%s\" failed with error: %d.\n\n",str,result);
 
return -1;
 
}
 
printf("Added application: %s\n",str);
 
condition.fieldKey = FWPM_CONDITION_ALE_APP_ID;
 
condition.matchType=FWP_MATCH_EQUAL;
 
condition.conditionValue.type=FWP_BYTE_BLOB_TYPE;
 
condition.conditionValue.byteBlob=applicationID;
 
}
 
}
 
}
 
#pragma endregion
 
#pragma region Добавление фильтра
 
result = FwpmFilterAdd0(engineHandle,&myFilter,NULL,&id);
 
if(result != ERROR_SUCCESS)
 
{
 
printf("Filter Added failed with error: %d\n\n",result);
 
return -1;
 
}
 
#pragma endregion
 
return id;
 
}
 
//Добавление вспомогательного фильтра на подуровень
 
void AddFilter(GUID subLayerGUID, HANDLE engineHandle, GUID guid)
 
{
 
DWORD result = NULL;
 
#pragma region Создание фильтра
 
FWPM_FILTER0_ myFilter={0};
 
RtlZeroMemory(&myFilter,sizeof(myFilter));
 
myFilter.action.type= FWP_ACTION_BLOCK;
 
myFilter.subLayerKey = subLayerGUID;
 
myFilter.weight.type = FWP_EMPTY;
 
myFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
 
myFilter.displayData.description = L"Secondary filter";
 
myFilter.displayData.name = L"Secondary filter";
 
myFilter.filterKey = guid;
 
myFilter.numFilterConditions = 0;
 
#pragma endregion
 
#pragma region Добавление фильтра
 
result = FwpmFilterAdd0(engineHandle,&myFilter,NULL,NULL);
 
if(result != ERROR_SUCCESS)
 
printf("Secondary filter added failed with error: %d\n\n",result);
 
else
 
printf("Secondary filter added successfully!\n");
 
#pragma endregion
 
}
 
//Удаление фильтров по сохраненным ID
 
void DelFilter(HANDLE engineHandle)
 
{
 
#pragma region Удаление фильтров по сохраненным ID
 
FILE *filterFile = fopen("C:\\Program Files\\Filter\\FilterList.iff","r");
 
if(!filterFile)
 
{
 
printf("File \"FilterList.iff\" dosen't exist!\n");
 
return;
 
}
 
UINT64 id = NULL;
 
DWORD result = NULL;
 
while(!feof(filterFile))
 
{
 
fscanf(filterFile,"%d",&id);//cчитываем i-ый адрес
 
//Если последняя строка, то выходим из цикла
 
if(feof(filterFile))
 
break;
 
if(id==0)
 
continue;
 
result = FwpmFilterDeleteById0(engineHandle,id);
 
if(result!=ERROR_SUCCESS)
 
printf("Filter (ID=%d) close failed with error: %s\n",id,result);
 
}
 
fclose(filterFile);
 
printf("Filters closed successfully!\n");
 
#pragma endregion
 
}
 
//Удаление фильтра по GUID
 
void DelFilter(HANDLE engineHandle, GUID ID)
 
{
 
#pragma region Удаление фильтра по GUID
 
DWORD result = FwpmFilterDeleteByKey0(engineHandle,&ID);
 
if(result != ERROR_SUCCESS)
 
{
 
if(result == FWP_E_FILTER_NOT_FOUND)
 
printf("Secondary filter doesn't exist.\n");
 
else
 
printf("Secondary filter closed failed with error: %d\n",result);
 
return;
 
}
 
printf("Secondary filter closed successfully!\n");
 
#pragma endregion
 
}
 
//Удаление подуровня
 
void DelSubLayer(HANDLE engineHandle, GUID _idSubLayer)
 
{
 
#pragma region Удаление подуровня
 
DWORD result = FwpmSubLayerDeleteByKey0(engineHandle,&_idSubLayer);
 
if(result != ERROR_SUCCESS)
 
printf("SubLayer closed failed with error: %d\n",result);
 
else
 
printf("SubLayer closed successfully!\n");
 
#pragma endregion
 
}
 
//Закрытие сеанса
 
void CloseSeans(HANDLE engineHandle)
 
{
 
#pragma region Закрытие сеанса
 
DWORD result = FwpmEngineClose0(engineHandle);
 
if(result != ERROR_SUCCESS)
 
printf("Engine close failed with error: %d\n",result);
 
else
 
printf("Engine closed successfully!\n");
 
system("pause");
 
#pragma endregion
 
}
 
//Запись в автозапуск
 
void AutoStart()
 
{
 
#pragma region Запись в автозапуск
 
DWORD result=NULL;
 
HKEY hKey = NULL;
 
result = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
 
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
 
NULL,
 
NULL,
 
REG_OPTION_VOLATILE,
 
KEY_ALL_ACCESS,
 
NULL,
 
&hKey,
 
NULL);
 
if(result!=ERROR_SUCCESS)
 
printf("Open failed with error: %d\n",result);
 
else
 
{
 
byte * buf = new byte[100];
 
buf = (LPBYTE)L"\"C:\\Program Files\\Filter\\ConsoleFilter.exe\"";
 
result = RegSetValueEx(hKey,
 
L"ConsoleFilter.exe",
 
NULL,
 
REG_SZ,
 
buf,
 
100);
 
if(result!=ERROR_SUCCESS)
 
printf("Add failed with error: %d\n",result);
 
result = RegCloseKey(hKey);
 
if(result!=ERROR_SUCCESS)
 
printf("Close failed with error: %d\n",result);
 
}
 
#pragma endregion
 
}
0
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
04.11.2014, 11:21 4
Лучший ответ Сообщение было отмечено NickoTin как решение

Решение

Здесь я постараюсь дать небольшой обзор WFP, а также затронуть некоторые
технические моменты. Без каких-либо претензий на полноту, точность и т.д.



Часть 1, Основы.

Что такое WFP ?

WFP расшифровывается как Windows Filtering Platform, платформа фильтрации Windows.
Внимание ! Не путать с технологией WPF из .NET !

WFP - это универсальная технология фильтрации сети, охватывающая все основные
уровни, от транспортного (TCP/UDP) до канального (Ethernet), и дающая разработчикам
массу интересных возможностей.



Почему WFP ?

До WFP разработчики фаерволов, спам-фильтров, родконтролей и других программ
такого плана были вынуждены использовать сложные, разрозненные, а часто просто
недокументированные способы, которые во многих отношениях не давали нужного
эффекта или требовали высокой квалификации и учета многочисленных деталей, с
которыми то и дело возникали проблемы при переносе на другие версии Windows.
Пример таких технологий - TDI, LSP, NDIS, а также старые Filter-Hook Drivers и
Firewall-Hook Drivers. В WFP многие типовые проблемы этих технологий были учтены,
некоторые решения вообще не имеют аналогов, либо тяжелореализуемы в TDI/LSP/NDIS.
Сама технология имеет достаточно простую и понятную объектную модель, хорошо
документирована и при желании ее может освоить человек весьма средней квалификации.

Добавлю, что WFP - единственная на сегодняшний день технология, позволяющая
полноценно фильтровать трафик от modern-приложений на Windows 8 и выше.
И не только. Например, TDI и LSP в настоящее время уже не справляются даже с
Internet Explorer 11 на Windows 7. Поэтому при выборе технологии для нового проекта
следует в первую очередь рассматривать WFP, и только потом (если есть причины,
например совместимость с XP или отсутствие каких-то важных функций WFP на
целевых версиях Windows) другие.



Краткая история.

Появилась в Windows Vista, с тех пор постепенно развивается и дополняется.
В Windows 7 появилась возможность фильтрации на уровне Ethernet, возможность
делать редиректы соединений (как исходящих, так и входящих), а также разные
вспомогательные функции, например для своевременной очистки ресурсов.
В Windows 8 появилось то, чего так долго ждали некоторые разработчики -
возможность многократно перекидывать соединение с одного прокси на другой.
Раньше из-за отсутствия этой возможности было почти нереальным иметь в системе,
к примеру, параллельно работающий фаервол и какой-нибудь спам-фильтр от
разных производителей. Есть и другие изменения, все они описаны в MSDN.



Ложка дегтя.

Лично для меня ложкой дегтя в WFP является то, что наиболее "вкусные" вещи в
ней появились только в Windows 7 - Windows 8 и для того, чтобы сделать
коммерческий продукт, реально совместимый со всей современной линейкой Windows,
не списывая со счетов XP и Vista, все равно приходится использовать TDI/LSP
вместе с WFP. Также я замечал, что документация местами недостаточно детальная, а
кое-где содержит ошибки. В итоге приходилось иногда закапываться надолго в
отладчик, чтобы узнать, в чем дело. И в WFP, как и в любой технологии, есть
недочеты и даже ошибки. Сам я с ними на практике не сталкивался, но не раз
слышал о проблемах с Teredo, например, в результате чего система выпадает в
синий экран (BSOD).



Документация и исходники.

Главный источник информации по WFP - конечно же, MSDN.
Обращаю внимание, что WFP имеет функции как в kernel mode, так и в user mode,
поэтому одну часть документации следует искать в разделе о программировании
драйверов, а вторую - в разделе "network programming" из WinAPI:

Windows Filtering Platform
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Windows Filtering Platform Callout Drivers
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Аналогично, исходники примеров для WFP есть как в Windows SDK Samples (diagevents и
msnfilter) и в Windows Driver Kit Samples (ddproxy, inspect, msnmntr и stmedit).
Вероятно, на MSDN Gallery можно найти и другие исходники, а где-нибудь на codeproject,
sourceforge и других опенсорсных хостингах - исходники проектов с использованием WFP.



В следующей части: Архитектура WFP, объектная модель, принципы работы.
4
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
04.11.2014, 15:19 5
Часть 2, Архитектура.



Введение.

Рассмотрим типичный жизненный цикл обмена информацией по протоколу TCP.

1. Приложение (клиент) вызывает функцию connect, указывая адрес и порт пункта
назначения, а также семейство и тип протокола. Или же (сервер) оно вызывает accept в
ожидании подключений.

2. Через некоторое время соединение установлено. Клиенту возвращается управление из
функции connect (ну или приходит соответствующий сигнал, как в случае с моделью
select или асинхронным I/O - это уже за рамками темы), серверу возвращается
новый сокет, связанный с клиентом.

3. Приложения обмениваются данными посредством функций send и recv.

4. Приложения закрывают свои концы соединений функцией shutdown.
Когда оба конца закрыты, связь завершается, соединение считается разомкнутым.

Представим, что мы пишем фильтр TCP-трафика, и нас на каждом шаге этой
последовательности интересуют определенные данные:

1. Адрес и порт пункта назначения, ID процесса, который выполнил connect/accept.
Имя и вообще контекст безопасности пользователя, которому принадлежит процесс.

2. Статус операции connect (успех или ошибка).

3. Буферы, передаваемые через send и recv.

Предположим, мы хотим блокировать нежелательные соединения (по набору запрещенных
IP-адресов и портов, а также по процессам или пользователям). Мы также хотим получать
уведомление о закрытии соединения (п.4), чтобы освободить все связанные с ним данные.

WFP для решения этой задачи предлагает несколько абстракций:

Layers - уровни фильтрации. Каждым уровнем обслуживается определенный этап обработки
соединения или передачи данных. Например, установка соединения обрабатывается на
уровне ALE (будет рассмотрен ниже), а работа с буферами send/recv - на уровне Stream.
У каждого уровня своя специфика и свой набор свойств, с которыми можно работать.
Часто ту информацию, что можно достать на одном уровне, уже нельзя увидеть на других.
Аналогично, не все функции, которые работают на одних уровнях, работают на других.

Sublayers - подуровни. Нужны для организации более гибкой стратегии фильтрации.
Например, можно зарегистрировать проверки на разных Sublayers и они будут выполняться
по очереди.

Filters - фильтры. Задают разные условия, при которых должна (или не должна) выполняться
фильтрация. Например, можно фильтровать только трафик, идущий на порт 80, а остальной
игнорировать. Фильтры определяют не только условия, но и действия, т.е. что делать с трафиком,
когда условие срабатывает. Основные операции: разрешить, заблокировать, задержать, либо
вызвать соответствующий Callout. Про Callout-ы будет написано ниже.

Conditions - условия, заданные в фильтре. В фильтре может быть несколько условий.

Shims - исполнительные объекты, расположенные во всех ключевых точках сетевого стека.
Если Filters и Conditions только задают правила, что делать с трафиком, то Shims отвечают за
само выполнение этих правил, т.е. блокировка, задержка трафика, вызов Callout-а и так далее.

Callout - блок функций, которые обрабатывают трафик и решают, что конкретно с ним делать.

Callout Driver - драйвер, реализующий один или несколько Callout-ов.

Provider - поставщик. Объединяет логически связанные Filters, Sublayers и Callouts,
принадлежащие одной программе, одной задаче/политике, или объединенные какими-то
другими общими признаками.

Filter Engine (Engine) - функциональное ядро WFP, которое управляет фильтрами, Shim-ами,
Callout-ами и остальными объектами технологии.

Base Filter Engine (BFE) - служба, отвечающая за координацию компонентов WFP, за
регистрацию новых фильтров, за хранение конфигурации, настройки безопасности и т.д.

Ссылки по теме:

WFP Architecture
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Object Model
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx



Как это работает.

Когда в сетевом стеке происходит какое-то событие, например установка или закрытие
соединения, приход дейтаграммы, получение буфера с данными и т.п., Filter Engine с
помощью Filters определяет, нужно ли как-то обрабатывать это событие. В терминах WFP
данный процесс называется классификацией (classify). Если хотя бы один Condition
срабатывает, вызывается Shim, который, в зависимости от того, что задано в фильтре,
выполняет нужное действие. В частности, Shim может вызвать ваш зарегистрированный
Callout, точнее, одну из его функций (из всего три).

Все остальное выполняет Callout, в соответствии с логикой фильтрации трафика.
Если, к примеру, Callout работает на уровне ALE, то в функцию ему будут переданы
адрес и порт пункта назначения и источника, тип протокола, ID приложения, полный
путь к exe и другая полезная информация. Если на уровне Stream, там будут другие
данные - направление трафика (входящий/исходящий), флаги и т.д. Далее Callout
может поступать с данными на свое усмотрение: блокировка, игнор, редирект, анализ,
задержка трафика и многое другое. Все ограничено лишь фантазией разработчика.

Выше была описана типичная последовательность операций при работе с TCP.
Давайте посмотрим, как она будет фильтроваться WFP.

1. Установка соединения (connect).
Здесь будут срабатывать фильтры на уровне FWPS_LAYER_ALE_AUTH_CONNECT_V4 .
Здесь же можно заблокировать соединение.

2. Соединение установлено.
Сработают фильтры на уровне FWPS_LAYER_ALE_FLOW_ESTABLISHE D_V4.
Добавлю, что на этом уровне блокировать коннект не рекомендуется, он создан
только для того, чтобы Callout получил уведомление о том, успешно или нет
создано соединение. Здесь же можно установить связь между коннектом и
потоком данных.

3. Передача данных.
Будут срабатывать фильтры на уровне FWPS_LAYER_STREAM_V4, причем как
для send (outgoing data), так и для recv (incoming data).

4. Закрытие соединения.
Если с потоком данных был ассоциирован контекст, будет вызвана одна из
функций Callout-а. Также на Windows 7 есть специальные уровни для
очистки ресурсов - FWPS_LAYER_ALE_RESOURCE_RELEAS E_V4.



Еще определения.

Flow (поток данных). Каждое соединение - это отдельный двунаправленный поток данных.
Если, к примеру, программа создает два коннекта на один и тот же адрес, вы будете в
драйвере видеть два разных потока данных, каждый со своим состоянием.

Flow Context (контекст потока). 64-битное число, ассоциированное с определенным
потоком данных. Нужно, чтобы отличать потоки друг от друга.

Fixed Values. Фиксированный набор аргументов, приходящий в Callout.
Описан здесь:

Data Field Identifiers
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Для каждого Layer-а свой набор аргументов.
Например, на уровне FWPS_LAYER_ALE_CONNECT_REDIREC T_V4 (установка соединения) через
Fixed Values можно получить имя пользователя, а на уровне FWPS_LAYER_STREAM_V4
его уже нет.

Meta Values. Аналогично Fixed Values.
Информация здесь:

Metadata Fields at Each Filtering Layer
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Итак, Callout, заглядывая в Fixed Values и Meta Values, решает, что делать с
данным событием/трафиком и предпринимает определенные действия, после чего весь
цикл повторяется заново. Доступ к буферам с данными осуществляется через
структуру NET_BUFFER (будет описана в следующих частях).
4
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
04.11.2014, 17:36  [ТС] 6
хорошо, тогда как мне написать фильтрацию в kernel-mode? я имею в виду, взять и переделать семпл-пример из WDK (strmedit or WFPSampler), или можно с нуля писать фильтрацию, дергая функции из fwpkclnt.sys (из файлов-заголовков а-ля ntifs.h, fwpmk.h и другие)?
я просто не могу понять подхода этого..
0
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
04.11.2014, 18:41 7
Для начала нужно зарегистрировать своего провайдера и связать с ним
нужные Sublayers, Callouts и Filters. Для этого есть такие функции:
FwpmProviderAdd0, FwpmSubLayerAdd0, FwpmFilterAdd0. Хэндл Filter Engine
получается вызовом FwpmEngineOpen0, лучше обернуть операцию установки и
удаления в транзакцию - FwpmTransactionBegin0 и FwpmTransactionCommit0.
Не забываем использовать флаг FWPM_XXXX_FLAG_PERSISTENT, тогда
регистрация драйвера сохранится после перезагрузки.
Обычно регистрацию выполняет установщик.

В самом Callout-е всего три функции: ClassifyFn, NotifyFn и FlowDelete.
ClassifyFn выполняет классификацию трафика и, к примеру, блокировку.
NotifyFn опциональна и нужна для получения уведомлений, происходящих с
Filter Engine (например, добавление новых фильтров). Ну а FlowDelete
вызывается при уничтожении Flow, чтобы иметь возможность очистить
связанные с ним ресурсы.

Для фильтрации TCP можно поступить так: зарегистрировать два Callout-а,
один на уровне FWPM_LAYER_ALE_AUTH_CONNECT_V4 (установка соединения),
второй на уровне FWPM_LAYER_STREAM_V4 (передача данных).
См. функцию FwpsCalloutRegister0, например.

Когда сработает Callout на ALE, получаем IP-порт из Fixed/Meta Values, и
если надо, блокируем коннект (FWP_ACTION_BLOCK). Если нужно анализировать
данные дальше, ничего не делаем. Дальше будет срабатывать Callout на
уровне Stream. Здесь нужно вытаскивать данные из буферов (см. третий
параметр функции ClassifyFn), анализировать их, и при случае блокировать
передачу (также FWP_ACTION_BLOCK). Все данные передаются в формате
NET_BUFFER, описание есть в MSDN и на CodeMachine.

Я собирался написать это в ближайших двух-трех частях, а уже потом
отвечать на вопросы.
2
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
04.11.2014, 18:56  [ТС] 8
охх... тогда жду части)
довольно познавательно!!!
0
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
05.11.2014, 22:56 9
Часть 3-а, Регистрация в системе.

Регистрация выполняется в режиме пользователя, обычно эта функция возлагается на
установщик программы. Впрочем, никто не мешает регистрировать драйвер каждый
раз при запуске программы, WFP дает такую возможность.

Регистрация начинается с функции FwpmEngineOpen0.

Кстати, небольшое отступление. В WFP, как и в некоторых других специфических
компонентах Windows, принята своя система именования: ПрефиксТипОбъектОперацияВерсия .

Префикс - Fwp, общий для подсистемы WFP.

Тип - означает тип операции, managed (m) или run-time (s).
Все, что managed, относится к user mode, а run-time относится к kernel mode.
Например, при регистрации из user mode в качестве Layer передается константа
FWPM_LAYER_INBOUND_IPPACKET_V4, а в kernel mode она же фигурирует под именем
FWPS_LAYER_INBOUND_IPPACKET_V4.

Объект - указывает сущность, к которой применяется операция.
В данном случае Engine.

Open - операция.

0 - номер версии.

Похожие соглашения можно встретить в Windows еще кое-где (например, в WDF).
Итак, FwpmEngineOpen0:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FWPM_SESSION0   Session;
HANDLE          hEngine;
 
RtlZeroMemory(&Session, sizeof (Session));
 
Session.displayData.name        = L"MyWfpSession";
Session.displayData.description = L"My WFP Session";
 
DWORD Status = FwpmEngineOpen0(
    NULL,
    RPC_C_AUTHN_DEFAULT,
    NULL,
    &Session,
    &hEngine
);
 
if (ERROR_SUCCESS != Status)
{
    THROW_EXC("FwpmEngineOpen0 failed with status 0x%.8lx.", Status);
}
Я не буду подробно разбирать параметры функции, т.к. все это есть в MSDN.
В простейшем случае Session достаточно заполнить нулями, указав только
displayData.name и displayData.description. Отдельно стоит отметить флаг
FWPM_SESSION_FLAG_DYNAMIC - при его использовании регистрация действует
до того момента, когда будет закрыт хэндл hEngine (или до того момента,
когда приложение завершится).

MSDN настоятельно рекомендует оборачивать операции установки и удаления
внутрь транзакции, и я не вижу причин, по которым не стоит следовать
этому совету. Для этого нам помогут функции FwpmTransactionBegin0 и
FwpmTransactionCommit0, а также FwpmTransactionAbort0. Все очень просто:
сразу после FwpmEngineOpen открываем транзакцию и все остальные объекты
регистрируем между Begin и Commit. Если что-то пошло не так, зовется
функция Abort, отменяющая все сделанные изменения.

Следующий необходимый шаг - регистрация провайдера.

Как нетрудно догадаться, для этого нужна функция FwpmProviderAdd0.
И здесь все достаточно тривиально, обойдемся без комментариев.
Обращаю внимание на флаг FWPM_PROVIDER_FLAG_PERSISTENT: если его не
поставить, то после перезагрузки компьютера можно с удивлением обнаружить,
что никакого провайдера в системе нет. Аналогичные флаги персистентности
есть у Filters, Callouts и Sublayers.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FWPM_PROVIDER0 Provider;
 
RtlZeroMemory(&Provider, sizeof (Provider));
 
Provider.providerKey             = GUID_MyWfpProvider;
Provider.displayData.name        = L"MyWfpProvider";
Provider.displayData.description = L"My WFP Provider for test application";
Provider.flags                   = FWPM_PROVIDER_FLAG_PERSISTENT;
 
Status = FwpmProviderAdd0(hEngine, &Provider, NULL);
 
if (ERROR_SUCCESS != Status)
{
    THROW_EXC("FwpmProviderAdd0 failed with status 0x%.8lx.", Status);
}
Добавляем Sublayer.

При чтении документации WFP становится не сразу понятно, зачем вообще нужны
подуровни и почему нельзя поставить свой фильтр на какой-нибудь существующий
уровень/подуровень. Ответ находится здесь:

Filter Arbitration
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Если вкратце, смысл вот в чем. Представьте, что на одном и том же подуровне
сидят два фильтра от разных провайдеров, которые анализирут трафик и решают,
пропускать его или нет. В этом случае, если верхний фильтр (т.е. тот, который
вызывается раньше другого) явным образом разрешит прохождение трафика
(FWP_ACTION_PERMIT), нижний фильтр вообще не будет участвовать в "голосовании".
Почему - спросите у разработчиков WFP. Так задумано. Но в таком случае это поведение
нарушает логику работы второго фильтра, который при других обстоятельствах
запретил бы трафик. Впрочем, вы можете посадить фильтр на какой-нибудь
предопределенный подуровень и не мучиться. Только потом не удивляйтесь, если
ваш Callout не всегда будет срабатывать.

Добавить Sublayer очень просто, как и все остальное:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FWPM_SUBLAYER0 Sublayer;
RtlZeroMemory(&Sublayer, sizeof (Sublayer));
 
Sublayer.subLayerKey             = GUID_MyWfpSublayer;
Sublayer.displayData.name        = L"MyWfpSublayer";
Sublayer.displayData.description = L"My WFP Sublayer";
Sublayer.flags                   = FWPM_SUBLAYER_FLAG_PERSISTENT;
Sublayer.providerKey             = (GUID *)&GUID_MyWfpProvider;
Sublayer.weight                  = 0x123;
 
Status = FwpmSubLayerAdd0(hEngine, &Sublayer, NULL);
 
if (ERROR_SUCCESS != Status)
{
    THROW_EXC("FwpmSublayerAdd0 failed with status 0x%.8lx.", Status);
}
Обращаю внимание на поле weight. Здесь устанавливается "вес" Sublayer-а.
Более "тяжелые" Sublayer-ы обрабатываются первыми.
Этот параметр нужен для упорядочивания своих Sublayer-ов, а вовсе не
для того, чтобы записать туда 0xFFFF в надежде быть всегда первым.

Регистрируем Callout:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FWPM_CALLOUT0 Callout;
RtlZeroMemory(&Callout, sizeof (Callout));
 
Callout.calloutKey              = GUID_MyWfpConnectCallout;
Callout.displayData.name        = L"MyWfpConnectCallout";
Callout.displayData.description = L"My WFP Connection Callout";
Callout.flags                   = FWPM_CALLOUT_FLAG_PERSISTENT;
Callout.providerKey             = (GUID *)&GUID_MyWfpProvider;
Callout.applicableLayer         = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
 
UINT32 CalloutId;
Status = FwpmCalloutAdd0(hEngine, &Callout, NULL, &CalloutId);
 
if (ERROR_SUCCESS != Status)
{
    THROW_EXC("FwpmCalloutAdd0 failed with status 0x%.8lx.", Status);
}
И снова все тривиально. FwpmCalloutAdd0 возвращает ID Callout-а.
Здесь я зарегистрировал Callout на уровне FWPM_LAYER_ALE_AUTH_CONNECT_V4 .
Вы можете использовать другой уровень, можете добавить еще один или
несколько Callout-ов или Sublayer-ов, WFP это позволяет.
4
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
05.11.2014, 22:59 10
Часть 3-б, Регистрация.

Регистрация фильтров.

Переходим к самому сложному этапу:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
UINT32 const NumConds = 2;
FWPM_FILTER_CONDITION0 Conds[NumConds];  
 
Conds[0].fieldKey               = FWPM_CONDITION_IP_PROTOCOL;
Conds[0].matchType              = FWP_MATCH_EQUAL;
Conds[0].conditionValue.type    = FWP_UINT8;
Conds[0].conditionValue.uint8   = IPPROTO_TCP;
 
Conds[1].fieldKey               = FWPM_CONDITION_IP_REMOTE_PORT;
Conds[1].matchType              = FWP_MATCH_EQUAL;
Conds[1].conditionValue.type    = FWP_UINT16;
Conds[1].conditionValue.uint16  = 80;
 
FWPM_FILTER0 Filter;
RtlZeroMemory(&Filter, sizeof (Filter));
 
Filter.providerKey             = NULL;
Filter.displayData.name        = L"MyWfpConnectFilter";
Filter.displayData.description = L"My WFP Connection Filter";
Filter.flags                   = FWPM_FILTER_FLAG_PERSISTENT;
Filter.layerKey                = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
Filter.subLayerKey             = GUID_MyWfpSublayer;
Filter.weight.type             = FWP_UINT8;
Filter.numFilterConditions     = NumConds;
Filter.filterCondition         = &Conds[0];
Filter.action.type             = FWP_ACTION_CALLOUT_INSPECTION;
Filter.action.calloutKey       = GUID_MyWfpConnectCallout;
 
UINT64 FilterId;
 
Filter.filterKey = GUID_MyWfpConnectFilter;
Filter.weight.uint8 = 1;
 
Status = FwpmFilterAdd0(hEngine, &Filter, NULL, &FilterId);
 
if (ERROR_SUCCESS != Status)
{
    THROW_EXC("FwpmFilterAdd0 failed with status 0x%.8lx.", Status);
}
Для данного фильтра мы создали два условия (Conditions): 1) протокол TCP;
2) порт 80. Таким образом, мы хотим ловить весь трафик, проходящий по TCP
на 80 порту (обычно HTTP).

В каждом условии fieldKey задает признак, который будет проверяться.
Их достаточно много:

Filtering Condition Identifiers
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Кроме этого, можно проверять также различные флаги, например на Windows 8 с
помощью флага FWP_CONDITION_FLAG_IS_PROXY_CO NNECTION можно проверить, является
ли соединение проксируемым. Полный список здесь:

Filtering Condition Flags
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Но будьте осторожны !
Как и в случае с Fixed Values / Meta Values, конкретные свойства и флаги
доступны только на конкретных уровнях.

Filtering Conditions Available at Each Filtering Layer
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

matchType задает тип проверки - равно/не равно, больше/меньше, проверка диапазона и т.д.
Например, если бы я вместо FWP_MATCH_EQUAL поставил FWP_MATCH_GREATER, фильтр
стал бы срабатывать на всех соединениях с номером порта больше 80. Вот список
доступных типов проверки:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef enum FWP_MATCH_TYPE_ { 
  FWP_MATCH_EQUAL,
  FWP_MATCH_GREATER,
  FWP_MATCH_LESS,
  FWP_MATCH_GREATER_OR_EQUAL,
  FWP_MATCH_LESS_OR_EQUAL,
  FWP_MATCH_RANGE,
  FWP_MATCH_FLAGS_ALL_SET,
  FWP_MATCH_FLAGS_ANY_SET,
  FWP_MATCH_FLAGS_NONE_SET,
  FWP_MATCH_EQUAL_CASE_INSENSITIVE,
  FWP_MATCH_NOT_EQUAL,
  FWP_MATCH_TYPE_MAX
} FWP_MATCH_TYPE;
conditionValue.type задает тип данных, а в conditionValue передается само
значение, которое требуется сравнивать.

Фильтр устанавливается на конкретном Sublayer-е (Filter.subLayerKey) и,
как и другие объекты, имеет схожие флаги. "Вес" фильтра имеет значение,
когда у вас на одном Sublayer сидят два или более фильтров. В других
случаях конкретное значение роли не играет. Но на всякий случай все
равно советую заглянуть сюда:

Filter Weight Assignment
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Filter Weight Identifiers
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Ну и ключевой момент: в action.type указывается тип действия, которое
будет выполняться при срабатывании фильтра. Возможных значений всего пять:

FWP_ACTION_BLOCK

Заблокировать трафик.

FWP_ACTION_PERMIT

Разрешить трафик.

FWP_ACTION_CALLOUT_TERMINATING
FWP_ACTION_CALLOUT_INSPECTION
FWP_ACTION_CALLOUT_UNKNOWN

Вызвать Callout. Разница между TERMINATING, INSPECTION и UNKNOWN только в
том, что позволено делать Callout-у. TERMINATING-Callout обязан явным образом
либо разрешить, либо заблокировать соединение. INSPECTION-Callout может
только наблюдать. UNKNOWN-Callout может, но не обязан, блокировать или
разрешать трафик.

Если вы ассоциируете с фильтром Callout, то должны указать его GUID в
поле action.calloutKey. Пока Callout Driver еще не написан, так что
делать этот фильтр ничего полезного не будет.

Кстати, здесь может возникнуть вопрос: а зачем вообще нужен Callout, если
можно создать правило и установить в фильтре action.type = FWP_ACTION_BLOCK,
блокируя нежелательные соединения ? Правильно, так можно и нужно делать,
если этого достаточно для вашей задачи. Добавлю, что в WFP есть несколько
предопределенных Callout-ов для некоторых задач, собраны они здесь:

Built-in Callout Identifiers
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Но все же FWP_ACTION_BLOCK и встроенных Callout-ов часто бывает недостаточно.
Если вам нужно анализировать, а тем более изменять трафик, работая с
буферами данных, то без написания Callout-драйвера не обойтись.

Итак, подытожим. Мы создаем хэндл Engine, открываем транзакцию, затем внутри
транзакции регистрируем своего провайдера, связываем с ним один или несколько
Sublayers, добавляем Callouts и Filters на нужные уровни/подуровни, остается
только позвать FwpmTransactionCommit0 и позакрывать хэндлы. С этого момента
ваши объекты вступают в "игру" в сетевом стеке Windows.

Отмена регистрации - обратная процедура, еще более простая.
Функции FwpmEngineOpen0 и FwpmTransactionBegin0 должны быть уже вам знакомы,
они и здесь используются. Для удаления фильтров используйте FwpmFilterDeleteByKey0,
для удаления Callout-ов - FwpmCalloutDeleteByKey0, затем можно удалить Sublayer -
FwpmSubLayerDeleteByKey, и наконец, провайдера - FwpmProviderDeleteByKey0,
после чего завершаете транзакцию и закрываете хэндлы.

В следующей части: реализация Callout в драйвере.
3
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
09.11.2014, 23:32 11
Часть 4-а, реализация Callout-драйвера.

Переходим к реализации Callout-драйвера.
Первое, что нужно сделать - создать устройство (DEVICE_OBJECT), для этого используется
функция IoCreateDevice. Далее вызываем FwpsCalloutRegister0 столько раз, сколько
Callout-ов нам требуется зарегистрировать:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
FWPS_CALLOUT0   CalloutInitData;
DEVICE_OBJECT * pDeviceObj;
NTSTATUS        Status;
    
Status = IoCreateDevice(
    pDriverObj,
    0,      // Size of device extension.
    NULL,   // Name of the device.
    FILE_DEVICE_UNKNOWN,
    0,      // Device characteristics.
    FALSE,  // Exclusive or not.
    &pDeviceObj
    );
        
if (!NT_SUCCESS(Status))
{
    LOG_ERROR(
        "IoCreateDevice failed with status 0x%.8lx.\r\n",
        Status
        );
    goto ErrorExit;
}
 
RtlZeroMemory(&CalloutInitData, sizeof (CalloutInitData));
 
CalloutInitData.calloutKey   = GUID_MyWfpConnectCallout;
CalloutInitData.flags        = 0; // Flags.
CalloutInitData.classifyFn   = OnClassify;
CalloutInitData.notifyFn     = OnNotify;
CalloutInitData.flowDeleteFn = OnFlowDelete;
    
Status = FwpsCalloutRegister0(
    pDeviceObj,
    &CalloutInitData,
    &ConnectCalloutId
    );
        
if (STATUS_SUCCESS != Status)
{
    LOG_ERROR(
        "FwpsCalloutRegister0 failed with status 0x%.8lx.\r\n",
        Status
        );
    goto ErrorExit;
}
При регистрации указывается GUID Callout-а, флаги, а также три стандартные функции:
classifyFn, notifyFn и flowDelete. Вы можете использовать одни и те же функции для
разных Callout-ов.

Кстати, в Windows 7 и выше доступна функция FwpsCalloutRegister1, а в Windows 8 -
FwpsCalloutRegister2. Они предоставляют более интересные возможности, например
редирект соединений.

Обращаю внимание на флаг FWP_CALLOUT_FLAG_CONDITIONAL_O N_FLOW: если установить
его, Callout будет срабатывать только для данных, с которыми связан определенный Flow Context.
Иными словами, можно выборочно контролировать определенный трафик, игнорируя остальные.

Также замечу, что если необходимые фильтры уже зарегистрированы, Callout начинает
действовать сразу же, еще до выхода из функции FwpsCalloutRegister0, так что если у вас есть
какие-то общие данные, разделяемые с Callout-ом, они должны быть инициализированы до того,
как эта функция будет вызвана.

Удалить Callout также просто: FwpsCalloutUnregisterById0, функция принимает единственный
аргумент - ID Callout-а. После удаления Callout-ов не забудьте удалить устройство,
созданное IoCreateDevice.
3
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
09.11.2014, 23:41 12
Часть 4-б, функции Callout-а.

Их всего три: classifyFn, notifyFn и flowDelete, причем notifyFn может быть реализована
очень просто, а flowDelete опциональна и вместо нее можно передать NULL (если вы не
работаете с Flow Context).

classifyFn
C
1
2
3
4
5
6
7
8
9
10
11
12
13
VOID
NTAPI
OnClassify(
    __in    FWPS_INCOMING_VALUES0 const           * pInFixedValues,
    __in    FWPS_INCOMING_METADATA_VALUES0 const  * pInMetaValues,
    __out   VOID                                  * pLayerData,
    __in    FWPS_FILTER0 const                    * pFilter,
    __in    UINT64                                  FlowContext,
    __out   FWPS_CLASSIFY_OUT0                    * pClassifyOut
    )
{
    // ...
}
В этой функции вы будете проводить большую часть времени, работая
над Callout-драйвером. Разберем по порядку параметры:

pInFixedValues - Массив Fixed Values (рассматривалось выше).

Вытаскивать данные из этого массива следует примерно так:
C
1
2
3
4
5
6
//
// Get remote IP address (ALE_FLOW_ESTABLISHED layer).
//
UINT32 Addr = pInFixedValues->incomingValue[
    FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS
    ].value.uint32;
Как я уже писал выше, состав Fixed Values для каждого уровня свой.

pInMetaValues - Массив Meta Values.

Перед тем, как обращаться к полям этого массива, следует проверить,
присутствует ли значение в нужном поле. Например:
C
1
2
3
4
5
6
if (FWPS_IS_METADATA_FIELD_PRESENT(pInMetaValues, FWPS_METADATA_FIELD_FLOW_HANDLE))
{
    //
    // Use pInMetaValues->flowHandle.
    //
}
pLayerData - указатель на данные (буферы).

Здесь может быть либо NULL, в зависимости от условий, либо указатель на
структуру FWPS_STREAM_CALLOUT_IO_PACKET0 (для stream-уровней, таких
как FWPS_LAYER_STREAM_V4), либо NET_BUFFER. Так или иначе все сведется к
работе с NET_BUFFER (будет описана в одной из следующих частей).

Состав данных, которые приходят в pLayerData, сильно зависит от уровня
фильтрации, на котором зарегистрирован Callout. Например, обрабатывая
TCP-поток на уровне FWPS_LAYER_STREAM_V4, вы не увидите в данных
заголовков IP-пакетов и т.п.

pFilter - указатель на Filter, по условию которого сработал ваш Callout.
Здесь находятся важные данные, которые нельзя проигнорировать.
Отнеситесь с вниманием к флагам FWPS_FILTER_FLAG_CLEAR_ACTION_ RIGHT и
FWPS_FILTER_FLAG_PERMIT_IF_CAL LOUT_UNREGISTERED, как описано здесь:

FWPS_FILTER0 structure
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Если FWPS_FILTER_FLAG_CLEAR_ACTION_ RIGHT установлен, вы должны сбросить флаг
FWPS_RIGHT_ACTION_WRITE в соответствующей структуре (будет описана ниже),
если завершаете classifyFn с кодом FWP_ACTION_PERMIT (разрешить) или
FWP_ACTION_BLOCK (заблокировать). Обычно сбрасывать FWPS_RIGHT_ACTION_WRITE
требуется только при FWP_ACTION_BLOCK.

Флаг FWPS_FILTER_FLAG_PERMIT_IF_CAL LOUT_UNREGISTERED означает, что если
Callout не зарегистрирован, вам следует пропустить трафик дальше, не блокируя его.

В поле action фильтра будет указан тип Callout-а, в зависимости от которого
вам разрешается или нет использовать соответствующие коды завершения, об этом я
уже писал выше: FWP_ACTION_CALLOUT_TERMINATING требует, чтобы Callout установил
для трафика FWP_ACTION_PERMIT или FWP_ACTION_BLOCK, FWP_ACTION_CALLOUT_INSPECTION
может только наблюдать (FWP_ACTION_CONTINUE) и т.д.

Вы не должны нарушать эти требования, иначе правильная система фильтрации не
гарантируется. На таких принципах, кстати, построено очень многое во взаимодействии
фильтрующих драйверов, не только сетевых: вам дается большая гибкость и разнообразие
средств, но вместе с тем вы должны придерживаться определенных правил и не ставить
"палки в колеса" другим драйверам.

FlowContext - название говорит само за себя. Здесь передается значение, связанное с
потоком данных, при условии, что вы задали его где-то раньше. Это значение вы можете
использовать на свое усмотрение, например передавать через него адрес каких-то структур
данных или функций, связанных именно с этим потоком, либо что-то еще. Размер поля
FlowContext - 64 бита, хватит на все, единственное ограничение - нельзя иметь Flow Context,
равный нулю.

Про Flow Context и потоки данных будет написано в одной из следующих частей.

pClassifyOut - сюда вы указываете, как хотите поступить с трафиком.

Поле actionType:

FWP_ACTION_PERMIT - разрешить;

FWP_ACTION_BLOCK - заблокировать;

FWP_ACTION_CONTINUE - передать запрос на рассмотрение в другой фильтр.

Вы можете установить любой код, если он не противоречит условиям action фильтра,
описанным выше. Обратите внимание, что если флаг FWPS_RIGHT_ACTION_WRITE в
поле rights сброшен, то вы не имеете права ничего писать в actionType, кроме
FWP_ACTION_BLOCK. То есть, либо пишете FWP_ACTION_BLOCK, либо оставляете
поле как есть, без изменений.

Попробуем сложить все вместе. Пример ниже подразумевает, что в фильтре указан
action.type FWP_ACTION_CALLOUT_UNKNOWN, т.е. Callout может возвращать и permit, и
block, и continue:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
VOID
NTAPI
OnClassify(
    __in    FWPS_INCOMING_VALUES0 const           * pInFixedValues,
    __in    FWPS_INCOMING_METADATA_VALUES0 const  * pInMetaValues,
    __out   VOID                                  * pLayerData,
    __in    FWPS_FILTER0 const                    * pFilter,
    __in    UINT64                                  FlowContext,
    __out   FWPS_CLASSIFY_OUT0                    * pClassifyOut
    )
{
    //
    // Check layer.
    //
    if (FWPS_LAYER_STREAM_V4 == pInFixedValues->layerId)
    {
        //
        // Working with fixed values.
        //
        UINT32 IpAddr = pInFixedValues->incomingValue[
            FWPS_FIELD_STREAM_V4_IP_REMOTE_ADDRESS
            ].value.uint32;
            
        // ...
        
        //
        // Working with meta values.
        //
        if (FWPS_IS_METADATA_FIELD_PRESENT(pInMetaValues, FWPS_METADATA_FIELD_PROCESS_ID ))
        {
            UINT32 ProcessId = pInMetaValues->processId;
            
            // ...
        }
    }
    
    //
    // Working with layer data (buffers).
    //    
    pIoPacket = (FWPS_STREAM_CALLOUT_IO_PACKET0 *)pLayerData
    
    // ...
    
    //
    // Applying filter decision.
    //
    
    switch (Decision)
    {
        case DECISION_PERMIT_TRAFFIC:
        {
            if (IS_FLAG_ENABLED(pClassifyOut->rights, FWPS_RIGHT_ACTION_WRITE))
            {
                pClassifyOut->actionType = FWP_ACTION_PERMIT;
            }
            
            if (FLAG_ENABLED(pFilter->flags, FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT))
            {
                CLEAR_FLAG(pClassifyOut->rights, FWPS_RIGHT_ACTION_WRITE);
            }
        }
        break;
        
        case DECISION_BLOCK_TRAFFIC:
        {
            pClassifyOut->actionType = FWP_ACTION_BLOCK;
            
            if (IS_FLAG_ENABLED(pFilter->flags, FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT))
            {
                CLEAR_FLAG(pClassifyOut->rights, FWPS_RIGHT_ACTION_WRITE);
            }
        }
        break;
        
        case DECISION_CONTINUE:
        {
            if (IS_FLAG_ENABLED(pClassifyOut->rights, FWPS_RIGHT_ACTION_WRITE))
            {
                pClassifyOut->actionType = FWP_ACTION_CONTINUE;
            }                       
        }
        break;
    }    
}
Как видите, ничего заумного. Разумеется, все самое интересное, что будет
составлять суть вашей программы, кроется за комментариями "//...".

Напоследок добавлю, что вы не можете просто взять и изменить данные в pLayerData.
Для подобных вещей в WFP принято несколько схем: "close-reinject", "drop-reinject", и т.д.
То есть, чтобы изменить данные, следует сначала заблокировать (FWP_ACTION_BLOCK)
нужный фрагмент (см. структуру FWPS_STREAM_CALLOUT_IO_PACKET0 ), а затем вставить
новый - FwpsStreamInjectAsync0. Если требуется отложить обработку трафика, например
задать вопрос пользователю в интерактивном режиме, следует использовать специальные
функции: на ALE-уровнях есть FwpsPendOperation0/FwpsCompleteOperation0, на Stream-
уровнях есть FwpsCloneStreamData0/FwpsStreamInjectAsync0 и т.д.

Собственно, здесь все достаточно хорошо расписано, еще и с примерами:

Inspecting Packet and Stream Data
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

notifyFn
C
1
2
3
4
5
6
7
8
9
10
NTSTATUS
NTAPI
OnNotify(
    __in    FWPS_CALLOUT_NOTIFY_TYPE                NotifyType,
    __in    GUID const                            * pFilterKey,
    __in    FWPS_FILTER0 const                    * pFilter
    )
{
    // ...
}
Очень простая функция.
Сюда приходят уведомления о добавлении и удалении фильтров:
FWPS_CALLOUT_NOTIFY_ADD_FILTER и FWPS_CALLOUT_NOTIFY_DELETE_FIL TER.

Все, что вам нужно - вернуть STATUS_SUCCESS, либо код ошибки,
если вы не хотите, чтобы фильтр был добавлен в Filter Engine.
Своего рода фильтр для фильтров.

flowDeleteFn
C
1
2
3
4
5
6
7
8
9
10
VOID
NTAPI
OnFlowDelete(
    __in    UINT16                                  LayerId,
    __in    UINT32                                  CalloutId,
    __in    UINT64                                  FlowContext
    )
{
    // ...
}
Эта функция вызывается, когда уничтожается поток данных.
Здесь последняя возможность очистить связанные с потоком данные, используя
значение FlowContext. Если вы не используете FlowContext, можете игнорировать
эту функцию или вообще установить ее в NULL при регистрации Callout-а.

Не забывайте, что все три функции Callout-а вызываются на IRQL <= DISPATCH_LEVEL.

В следующей части: потоки данных, работа с Flow Context,
как отличить данные одного соединения от другого.
4
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
18.11.2014, 15:04  [ТС] 13
ждём продолжения))
и если можно, чуть детальной про IRQL и unreferenced parameter...
0
Почетный модератор
Эксперт .NET
8663 / 3615 / 404
Регистрация: 14.06.2010
Сообщений: 4,513
Записей в блоге: 9
18.11.2014, 21:45 14
X-Cod, продолжение будет здесь Краткий обзор Windows Filtering Platform, подписывайтесь
1
14 / 13 / 1
Регистрация: 30.09.2011
Сообщений: 160
19.11.2014, 12:45  [ТС] 15
NickoTin, пасяб)
0
0 / 0 / 0
Регистрация: 04.11.2014
Сообщений: 3
30.12.2014, 14:55 16
Добрый день. По документации, примерам ddk, и информации из Вашей статьи дошел до такого: драйвер(регистрирует 3 callouts - FWPM_LAYER_ALE_AUTH_CONNECT_V4 , FWPM_LAYER_ALE_FLOW_ESTABLISHE D_V4, FWPM_LAYER_STREAM_V4). На уровне connect с помощью WokrItem получаю UserName, Domain процесса. На уровне
0
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
30.12.2014, 15:58 17
Цитата Сообщение от gontarenko Посмотреть сообщение
На уровне...
Сообщение разбилось на IP-пакеты
Ждем полного вопроса.
0
0 / 0 / 0
Регистрация: 04.11.2014
Сообщений: 3
30.12.2014, 16:05 18
Добрый день. По документации, примерам ddk, и информации из Вашей статьи дошел до: драйвер (регистрирует 3 колаута - FWPM_LAYER_ALE_AUTH_CONNECT_V4 , FWPM_LAYER_ALE_FLOW_ESTABLISHE D_V4, FWPM_LAYER_STREAM_V4).
- на уровне FWPM_LAYER_ALE_AUTH_CONNECT_V4 с помощью WorkItems получаю UserName, Domain и путь к файлу процесса, ассоциирую информацию с уровнем FLOW_ESTABLISHED;
- на уровне FWPM_LAYER_ALE_FLOW_ESTABLISHE D_V4 получаю удалённый адрес и порт, ассоциирую информацию с уровнем STREAM;
- на уровне FWPM_LAYER_STREAM_V4 уходит/приходит http трафик;

Хочу добиться сбора url и названия посещённых страниц.
Что делаю: использую RTL_AVL_TABLE для хранения информации о соединениях;
При ESTABLISHED создаю в RTL_AVL_TABLE элемент(также записываю в него данные path, user, domain, remote/local port,...);
При STREAM изменяю(дополняю) элемент в RTL_AVL_TABLE, запросом GET и ответом на него. (POST, HEAD и др. игнорирую);
При STREAM_DELETE помечаю элемент в RTL_AVL_TABLE как completed для дальнейшей работы над ним;

Создание, изменение, пометка элемента как completed происходит в 3х разных WorkItem -ах. Как я понял они работают асинхронно;
Иногда получается что код из Stream_WorkItem выполняется раньше чем код Established_WorkItem;

В голову приходит такое решение:
1) сделать отдельный поток и через список со спинлоком передавать в него информацию из ESTABLISHED, STREAM и STREAM_DELETE . Таким образом добиться обработки ESTABLISHED->STREAM->STREAM_DELETE по порядку;
2) сделать вместо 3х WorkItem-ов сделать один WorkItem, и возможно это решит данную проблему(хотя не совсем уверен);

Какое из решений будет правильнее с точки зрения производительности(либо оба неправильные), с учётом парсинга http страницы для получения заголовка страницы(<title>)?

Заранее спасибо.

Добавлено через 54 секунды
)))
0
Ушел с форума
Эксперт С++
16290 / 7356 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
30.12.2014, 16:15 19
А что мешает парсить поток данных прямо из classifyFn ?
0
0 / 0 / 0
Регистрация: 04.11.2014
Сообщений: 3
30.12.2014, 16:28 20
Хотя да, я что-то сильно намудрил с воркайтемами. Вся информация передаётся с CONNECT в ESTABLISHED, а с ESTABLISHED в STREAMclassify.

Еще раз спасибо.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.12.2014, 16:28

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Организация URL адресов
Добрый день. Столкнулся с такой проблемой ... Как правильно организовать URL ? Допустим есть...

Фильтрация MAC адресов в модеме DLINK 2540U
Добрый день. Есть модем DLINK 2540U, настроенный в режиме роутера. Возможно ли в настройках модема...

Смена URL адресов страниц
Имеется сайт (интернет-магазин) В индекс попал порядка месяца назад... постепенно дорабатывается....

Создание дружественных адресов URL
Добрый день Пытаюсь на сайте с помощью mod_rewrite переделать URL. RewriteRule...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.