博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微软引入了两种新的网络过滤系统,WFP和NDISfilter
阅读量:7165 次
发布时间:2019-06-29

本文共 10807 字,大约阅读时间需要 36 分钟。

Windows 8
是微软公司推出的最新的客户端OS,内部名称
Windows NT 80
。相对于
Windows NT 5.x
,其网络结构变化非常大,原有的
TDI
NDIS
系统挂接方法不再适用。在Windows8系统中,微软引入了两种新的网络过滤系统,
WFP
NDISfilter

WFP (Windows Filtering Platform)




其包含从用户态到核心态的一系列应用层,根据需要可以在某一层设置回调函数拦截数据。

1、  callout

callout
WFP
系统提供的扩展其功能的一种机制,
callout
由一组
callout
函数组成,每组有三种函数,

ClassifyFunction
,处理收到的网络数据,例如端口号IP
地址等。NotifyFunction,处理加载、删除callout事件。

FlowDeleteFunction
,删除层与层之间关联的上下文。

callout
由callout
驱动具体实现,每个驱动可以注册多个callout。

2
、WFP
的层和层数据

WFP
有很多层,
每一层分成若干子层,具体有哪些请参阅微软文档,我以
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
层为例进行讲解。这层位于
ALE
层,是其子层之一。面向连接的应用程序准备连接,面向无连接的程序准备通信,都发生在这一层。假如在这里拒绝了上述操作,应用程序就不能访问网络,这类似以前的
TDI
程序的
Create
事件,就是应用程序访问网络的请求刚到协议栈还没有处理。
WFP
每一层都有其特定的数据,根据这些数据又有特定的过滤条件,例如这层包括
FWPS_METADATA_FIELD_PROCESS_ID
类型数据,这个类型由
UINT64
类型数据定义,表示和本次网络访问请求相关的进程
ID
,可是
FWPS_LAYER_INBOUND_IPPACKET_V4
层就不包括这一类型的数据,其实
FWPS_LAYER_INBOUND_IPPACKET_V4
已经到了
IP
层,这里进程
ID
已经没用了。因此在
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
蹭可以以进程
ID
作为过滤条件,而到了
FWPS_LAYER_INBOUND_IPPACKET_V4
层就不能用进程
ID
作为过滤条件了。每一层都有哪些数据类型,根据这些数据有哪些过滤条件可用,请参阅微软的
WFP
文档层标识符等章节。

综上,
WFP
系统很像一个已经有了数据过滤引擎的防火墙,但是没有规则。我们编写用户层的程序给
WFP
引擎设置规则,编写核心态的
callout
驱动处理
WFP
抓到的网络数据包。根据微软的文档所示,
WFP
能够到达
IP
层,假如我们想进行
MAC
层的处理,就必须利用
NDISfilter
驱动。

3、
应用WFP
实现应用程序访问网络时提示

这是个人防火墙的基本功能之一,当有应用程序访问网络时询问用户是否允许。首先我们编写一个callout
驱动,用来处理WFP抓到的网络数据。由于WFP抓到的数据只送到callout驱动不会送到用户层程序,所以这里必须用驱动根据数据判定放行还是阻止。Callout驱动向系统注册callout函数,

    
FWPS_CALLOUT0
 
sCallout
;

    sCallout.
calloutKey = *
calloutKey;
    
sCallout.
flags = 
flags;
    
sCallout.
classifyFn = 
ClassifyFunction; //在实例程序代码中是
MonitorCoFlowEstablishedCalloutV4
    
sCallout.
notifyFn = 
NotifyFunction;
    
sCallout.
flowDeleteFn = 
FlowDeleteFunction;
status
 = 
FwpsCalloutRegister0
(
deviceObject
, &
sCallout
calloutId
);

FWPS_CALLOUT0
结构用来组织一组callout
函数,之后用FwpsCalloutRegister0函数注册。这里详细介绍下ClassifyFunction函数,这个函数主要处理网络数据包,

NTSTATUS MonitorCoFlowEstablishedCalloutV4(
   IN const FWPS_INCOMING_VALUES0* inFixedValues,//WFP传进来的本层特有的数据
   IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,//本层相关的扩展数据
   IN VOID* packet,
   IN const FWPS_FILTER0* filter,
   IN UINT64 flowContext,
   OUT FWPS_CLASSIFY_OUT0* classifyOut//用这个结构里的字段告知WFP对数据包做出处理
)
{
   
NTSTATUS 
status = 
STATUS_SUCCESS;
   
UINT64   
flowHandle;
   
UINT64   
flowContextLocal;
   
UINT32   
index;
   
UINT32   
LocalIPADDRv4,
remoteIPADDRv4;
   
USHORT   
LocalPort,
remotePort;

   
UNREFERENCED_PARAMETER(
packet);
   
UNREFERENCED_PARAMETER(
filter);
   
UNREFERENCED_PARAMETER(
flowContext);

   
index = 
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS;
   
LocalIPADDRv4 = 
inFixedValues->
incomingValue[
index].
value.
uint32;

   
index = 
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT;
   
LocalPort = 
inFixedValues->
incomingValue[
index].
value.
uint16;

   
index = 
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS;
   
remoteIPADDRv4 = 
inFixedValues->
incomingValue[
index].
value.
uint32;

   
index = 
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT;
   
remotePort = 
inFixedValues->
incomingValue[
index].
value.
uint16;

   
DbgPrint(
"BaseTDI: LocalIP %lx LocalPort %d \n remoteIP %lx remotePort %d",
LocalIPADDRv4,
LocalPort,
       
remoteIPADDRv4,
remotePort);
   
DbgPrint(
"BaseTDI: PID %d ,PID's PATH %s",
inMetaValues->
processId,
inMetaValues->
processPath->
data);
   
DbgPrint(
"\n");

   
if (
monitoringEnabled)
   {
        
//
访问规则程序代码,在这里通知用户态程序
        AskUser(LocalIP, LocalPort, remoteIP, remotePort,
PID);
        If 
允许
          
classifyOut->
actionType = 
FWP_ACTION_PERMIT;
 //允许发送或接收
        else
          
classifyOut->
actionType = 
FWP_ACTION_BLOCK;
 //不允许发送或接收
   }
  
return 
status;
}
在完成callout驱动后,下面介绍用户态程序如何设置WFP系统。

设置的大体流程如下文所示,



主要程序代码讲解,

//
WFP
系统添加
callout

DWORD 
WFPAppAddCallouts()
{
    
FWPM_CALLOUT0 
callout;
    
DWORD 
result;
    
FWPM_DISPLAY_DATA0 
displayData;
    
HANDLE 
engineHandle = 
NULL;
    
FWPM_SESSION0 
session;
    
//
初始化一次会话
    
RtlZeroMemory(&
session
sizeof(
FWPM_SESSION0));
    
session.
displayData.
name = 
L
"TEMP WFP Session";
    
session.
displayData.
description = 
L
"For Adding callouts";

    
//
创建WFP引擎句柄
    
result =  
FwpmEngineOpen0(
        
NULL,
        
RPC_C_AUTHN_WINNT,
        
NULL,
        &
session,
        &
engineHandle
        );
    
if (
NO_ERROR != 
result)
    {
goto 
cleanup;}
    
//
开始与引擎交互
    
result = 
FwpmTransactionBegin0(
engineHandle
0);
    
if (
NO_ERROR != 
result)
    {
goto 
abort;    }
ADD CALLOUT
    
RtlZeroMemory(&
callout
sizeof(
FWPM_CALLOUT0));
    
displayData.
description = 
MONITOR_FLOW_ESTABLISHED_CALLOUT_DESCRIPTION;
    
displayData.
name = 
MONITOR_FLOW_ESTABLISHED_CALLOUT_NAME;

    
callout.
calloutKey = 
TEMP_MONITOR_FLOW_ESTABLISHED_CALLOUT_V4;
    
callout.
displayData = 
displayData;
    
callout.
applicableLayer = 
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    
callout.
flags = 
FWPM_CALLOUT_FLAG_PERSISTENT
//flags
置这个标志表示callout始终被WFP加载
    
result = 
FwpmCalloutAdd0(
engineHandle, &
callout
NULL
NULL);
    
if (
NO_ERROR != 
result)
    {
goto 
abort;    }
//
结束本次会话
    
result = 
FwpmTransactionCommit0(
engineHandle);
    
if (
NO_ERROR == 
result)
    {;}
    
goto 
cleanup;
abort:

    
//
说明本次会话失败
    
result = 
FwpmTransactionAbort0(
engineHandle);
    
if (
NO_ERROR == 
result)
    {;}
cleanup:
    
//
关闭引擎
    
if (
engineHandle)
    {
        
FwpmEngineClose0(
engineHandle);
    }
    
return 
result;
}

//
WFP
系统添加
filter

DWORD
WFPAppAddFilters(
IN  
HANDLE 
engineHandle
/*,IN FWP_BYTE_BLOB* applicationPath*/)                 
{
    
DWORD 
result = 
NO_ERROR;
    
FWPM_SUBLAYER0 
monitorSubLayer;
    
FWPM_FILTER0 
filter;
    
FWPM_FILTER_CONDITION0 
filterConditions[
1]; 
//
需要几条规则就定义几条
    
//
初始化过滤条件
    
RtlZeroMemory(
filterConditions
sizeof(
filterConditions));
    
filterConditions[
0].
fieldKey = 
FWPM_CONDITION_IP_PROTOCOL;//所有IP协议数据
    
filterConditions[
0].
matchType = 
FWP_MATCH_GREATER_OR_EQUAL;
//
匹配度,大于,小于,大于等于
    
filterConditions[
0].
conditionValue.
type = 
FWP_UINT8;
    
filterConditions[
0].
conditionValue.
uint8 = 
IPPROTO_IP;

    
//
初始化子层
    
RtlZeroMemory(&
monitorSubLayer
sizeof(
FWPM_SUBLAYER0));
    
monitorSubLayer.
subLayerKey = 
TEMP_MONITOR_SUBLAYER;
    
monitorSubLayer.
displayData.
name = 
L
"TEMP Monitor Sub layer";
    
monitorSubLayer.
displayData.
description = 
L
"TEMP Monitor Sub layer";
    
monitorSubLayer.
flags = 
0;
//FWMP_SUBLAYER_FLAG_PERSISTENT;
    
// We don't really mind what the order of invocation is.
    
monitorSubLayer.
weight = 
0;

    
//
与WFP引擎开始一次会话
    
result = 
FwpmTransactionBegin0(
engineHandle
0);
    
if (
NO_ERROR != 
result)
    {
goto 
abort;}
    
//
增加一个子层
    
result = 
FwpmSubLayerAdd0(
engineHandle, &
monitorSubLayer
NULL);
    
if (
NO_ERROR != 
result)
    {
goto 
abort;}

FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
    
RtlZeroMemory(&
filter
sizeof(
FWPM_FILTER0));

    
filter.
layerKey = 
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    
filter.
displayData.
name = 
L
"Flow established filter.";
    
filter.
displayData.
description = 
L
"Sets up flow for traffic that we are interested in.";
    
filter.
action.
type = 
FWP_ACTION_CALLOUT_INSPECTION
//
表示把符合条件数据包交给callout处理
    
filter.
action.
calloutKey = 
TEMP_MONITOR_FLOW_ESTABLISHED_CALLOUT_V4;
    
filter.
filterCondition = 
filterConditions;
    
filter.
subLayerKey = 
monitorSubLayer.
subLayerKey;
    
filter.
weight.
type = 
FWP_EMPTY
//
系统自动设置weight。weight值越大加载越靠前
    
filter.
numFilterConditions = 
1;//过滤条件数

    
result = 
FwpmFilterAdd0(
engineHandle,
        &
filter,
        
NULL,
        &(
filterID[
0]));

    
if (
NO_ERROR != 
result)
    {
goto 
abort;}

//
结束本次会话
    
result = 
FwpmTransactionCommit0(
engineHandle);
    
if (
NO_ERROR == 
result)
    {;}
    
goto 
cleanup;
abort:
//
说明本次会话失败
    
result = 
FwpmTransactionAbort0(
engineHandle);
    
if (
NO_ERROR == 
result)
    {;}
cleanup:
    
return 
result;
}
二、NDISfilter
NDISfilter是利用系统提供的NDIS过滤引擎,获得MAC级别的网络数据包(这里可以看出WFP,NDISfilter,还有本文未提到的FileSystemMiniFilter,他们都是利用了微软提供的过滤引擎,向其注册回调函数,得到数据后处理)。关键程序代码说明,其中的详细数据结构请参阅微软文档NDISfilter一节,

NDIS_FILTER_DRIVER_CHARACTERISTICS      
FChars;

NdisZeroMemory(&
FChars
sizeof(
NDIS_FILTER_DRIVER_CHARACTERISTICS));
        
FChars.
Header.
Type = 
NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
        
FChars.
Header.
Size = 
sizeof(
NDIS_FILTER_DRIVER_CHARACTERISTICS);
        
FChars.
Header.
Revision = 
NDIS_FILTER_CHARACTERISTICS_REVISION_1;
        
FChars.
MajorNdisVersion = 
FILTER_MAJOR_NDIS_VERSION;
        
FChars.
MinorNdisVersion = 
FILTER_MINOR_NDIS_VERSION;
        
FChars.
MajorDriverVersion = 
1;
        
FChars.
MinorDriverVersion = 
0;
        
FChars.
Flags = 
0;

        
FChars.
FriendlyName = 
FriendlyName;
        
FChars.
UniqueName = 
UniqueName;
        
FChars.
ServiceName = 
ServiceName;

        
FChars.
SetOptionsHandler = 
FilterRegisterOptions;        
        
FChars.
AttachHandler = 
FilterAttach;//
假如是我们想挂接的网络介质,就在这里通知系统挂接
        
FChars.
DetachHandler = 
FilterDetach;
        
FChars.
RestartHandler = 
FilterRestart;
        
FChars.
PauseHandler = 
FilterPause;
        
FChars.
SetFilterModuleOptionsHandler = 
FilterSetModuleOptions;           
        
FChars.
OidRequestHandler = 
FilterOidRequest;
        
FChars.
OidRequestCompleteHandler = 
FilterOidRequestComplete;
        
FChars.
CancelOidRequestHandler = 
FilterCancelOidRequest;

        
FChars.
SendNetBufferListsHandler = 
FilterSendNetBufferLists;
//
发送回调函数     
        
FChars.
ReturnNetBufferListsHandler = 
FilterReturnNetBufferLists;
        
FChars.
SendNetBufferListsCompleteHandler = 
FilterSendNetBufferListsComplete;
        
FChars.
ReceiveNetBufferListsHandler = 
FilterReceiveNetBufferLists;
//
接收回调函数
        
FChars.
DevicePnPEventNotifyHandler = 
FilterDevicePnPEventNotify;
        
FChars.
NetPnPEventHandler = 
FilterNetPnPEvent;
        
FChars.
StatusHandler = 
FilterStatus;
        
FChars.
CancelSendNetBufferListsHandler = 
FilterCancelSendNetBufferLists;

NDIS_FILTER_DRIVER_CHARACTERISTICS
这个结构用来组织NDISfilter功能函数供NDIS系统回调,例如
FilterSendNetBufferLists
,发送数据回调函数,NDIS发送MAC帧时回调这个函数,相应数据可以在这个函数里得到处理,之后还给NDIS系统继续处理。
VOID
FilterSendNetBufferLists(
        
IN  
NDIS_HANDLE         
FilterModuleContext,
        
IN  
PNET_BUFFER_LIST    
NetBufferLists,
        
IN  
NDIS_PORT_NUMBER    
PortNumber,
        
IN  
ULONG               
SendFlags
        )
{
    
PMS_FILTER          
pFilter = (
PMS_FILTER)
FilterModuleContext;
    
NDIS_STATUS         
Status = 
NDIS_STATUS_SUCCESS;
    
PNET_BUFFER_LIST    
CurrNbl;
BOOLEAN             
DispatchLevel;
//
这里开始解释PNET_BUFFER_LIST指向的网络数据,并显示如何获得MAC地址
    PNET_BUFFER_LIST      
pNetBufList,
pNextNetBufList;
    
PMDL                  
pMdl;
    
PNDISPROT_ETH_HEADER  
pEthHeader = 
NULL;
    
ULONG                 
TotalLength,
Offset,
BufferLength;

    
pNetBufList = 
NetBufferLists;

    
while (
pNetBufList != 
NULL)
    {
        
pNextNetBufList = 
NET_BUFFER_LIST_NEXT_NBL (
pNetBufList);
        
//
得到当前和包相关的MDL,MDL里即MAC帧,详细的NET_BUFFER_LIST结构请参阅微软相关文档
        
pMdl = 
NET_BUFFER_CURRENT_MDL(
NET_BUFFER_LIST_FIRST_NB(
pNetBufList));   
        
TotalLength = 
NET_BUFFER_DATA_LENGTH(
NET_BUFFER_LIST_FIRST_NB(
pNetBufList));
        
Offset = 
NET_BUFFER_CURRENT_MDL_OFFSET(
NET_BUFFER_LIST_FIRST_NB(
pNetBufList));
        
BufferLength = 
0;
        
do
        {
            
ASSERT(
pMdl != 
NULL);
            
if (
pMdl)
            {
                
NdisQueryMdl(
                    
pMdl,
                    &
pEthHeader,
                    &
BufferLength,
                    
NormalPagePriority);
            }

            
if (
pEthHeader == 
NULL)
            {
                
BufferLength = 
0;
                
break;
            }

            
if (
BufferLength == 
0)
            {
                
break;
            }
            
ASSERT(
BufferLength > 
Offset);

            
BufferLength -= 
Offset;
            
pEthHeader = (
PNDISPROT_ETH_HEADER)((
PUCHAR)
pEthHeader + 
Offset);

            
DbgPrint(
"DstMAC %x-%x-%x-%x-%x-%x",
pEthHeader->
DstAddr[
0],
                
pEthHeader->
DstAddr[
1],
pEthHeader->
DstAddr[
2],
                
pEthHeader->
DstAddr[
3],
pEthHeader->
DstAddr[
4],
                
pEthHeader->
DstAddr[
5]);

            
DbgPrint(
"srcMAC %x-%x-%x-%x-%x-%x",
pEthHeader->
SrcAddr[
0],
                
pEthHeader->
SrcAddr[
1],
pEthHeader->
SrcAddr[
2],
                
pEthHeader->
SrcAddr[
3],
pEthHeader->
SrcAddr[
4],
                
pEthHeader->
SrcAddr[
5]);
            
DbgPrint(
"\n");

            
if (
BufferLength < 
sizeof(
NDISPROT_ETH_HEADER))
            {
                
break;
            }
        }
while (
FALSE);
        
pNetBufList = 
pNextNetBufList;
    }
}

转载于:https://www.cnblogs.com/vcerror/p/4289034.html

你可能感兴趣的文章