Win64 驱动内核编程-16.WFP网络监控驱动(防火墙)
WFP驅動監控網絡
? ? WFP?是微軟推出來替代?TDI?HOOK、NDIS?HOOK?等攔截網絡通信的方案,WFP?的框架非常龐大,在?RING3?和?RING0?各有一套類似的函數,令人興奮的是,即使在?R3?使用?WFP,也可以做到全局攔截訪問網絡。由于?WFP?的范圍太廣,實在難以一言概括,感興趣的朋友可以自行到?MSDN?上查看微軟對它的官方概述。本文的目的,是給大家理順?WFP?的框架,并利用?WFP?攔截指定進程訪問網絡,或攔截對指定?IP?地址/端口的訪問。
? ? 一個標準的?WFP?程序大體是這樣子的:?首先使用?FwpmEngineOpen??開啟?WFP??引擎?(獲得個?一個?WFP??的?使用?句柄)?)用?,然后用?FwpmTransactionBegin??設置?對網絡通信?內容?的?過濾?權限(是只讀)?還是允許修改)?,?然后用?FwpsCalloutRegister?、FwpmCalloutAdd?、FwpmFilterAdd??選擇你?要過濾的內容,?并?添加?過濾器對象?和?回調函數,?最后用?用?FwpmTransactionCommit??確認剛才的內容,?讓?剛才添加的?回調函數?開始生效?。?當用?你不用?WFP??的?時候,?就要用FwpmFilterDeleteById?、FwpmCalloutDeleteById?、FwpsCalloutUnregisterById??把你剛才添加的過濾器對象和回調函數刪除掉,然后用?用?FwpmEngineClose??關閉?WFP?擎?引擎?(?類似于?關閉句柄)?)。
? ? ?一個概述已經是這樣子了,實現起來就更加麻煩了。為了方便大家學習,作者(胡文亮)已經把微軟的?WFP?實例進行了最大簡化,并把核心的注冊回調功能封裝成了一個函數。下面一步一步進行分析。在具體分析之前,有一些重要的內容需要先說明,否則后面的內容肯定會讓大家一頭霧水。一、WFP??一次性?要?注冊的回調函數?不是?1??個?,而是?3??個?。?但只有?1??個?是?“?事前?”回調,?另外?2??個?都是?“?事后?”?回調?(一般?只?使用“?事前?”?回調?,不使用“?事后?”回調)?)。二、WFP??能過濾?的內容很多,你必須選擇一個?感興趣的內容。?這個“?感?興趣?的?內容”?用官話來說叫做?“過濾?條件標志?”,?這貨?其實?是一個?常量?,?由于我們是?要過濾進程聯網,?而且?一般都是用?IPV4??協議,所以?“過濾?條件標志?”為?為?FWPM_LAYER_ALE_AUTH_CONNECT_V4?(?在?這個
頁面?可以的?查到所有的?“過濾?條件標志”?”)?)。三、?你個?必須為這個?“過濾?條件標志?”指定一個?GUID?,當然?GUID??的?值?隨便設置?就行?,只要?在?系統范圍內不重復。?。?代碼中的?GUID??就是?隨意設定的,它的名稱為:GUID_ALE_AUTH_CONNECT_CALLOUT_V4。
?
我用的VS2015+wdk10開發的,VS15里面需要設置這些東西:
添加庫:ntoskrnl.lib?ndis.lib?fwpkclnt.lib?uuid.lib
添加宏定義:NDIS_SUPPORT_NDIS6
?注冊的代碼:
NTSTATUS RegisterCalloutForLayer (IN const GUID* layerKey,IN const GUID* calloutKey,IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,IN FWPS_CALLOUT_NOTIFY_FN notifyFn,IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,OUT UINT32* calloutId,OUT UINT64* filterId ) { NTSTATUS status = STATUS_SUCCESS; FWPS_CALLOUT sCallout = {0}; FWPM_FILTER mFilter = {0}; FWPM_FILTER_CONDITION mFilter_condition[1] = {0}; FWPM_CALLOUT mCallout = {0}; FWPM_DISPLAY_DATA mDispData = {0}; BOOLEAN bCalloutRegistered = FALSE; sCallout.calloutKey = *calloutKey; sCallout.classifyFn = classifyFn; sCallout.flowDeleteFn = flowDeleteNotifyFn; sCallout.notifyFn = notifyFn; //要使用哪個設備對象注冊 status = FwpsCalloutRegister( gDevObj,&sCallout,calloutId ); if( !NT_SUCCESS(status)) goto exit; bCalloutRegistered = TRUE; mDispData.name = L"WFP TEST"; mDispData.description = L"TESLA.ANGELA's WFP TEST"; //你感興趣的內容 mCallout.applicableLayer = *layerKey; //你感興趣的內容的GUID mCallout.calloutKey = *calloutKey; mCallout.displayData = mDispData; //添加回調函數 status = FwpmCalloutAdd( gEngineHandle,&mCallout,NULL,NULL); if( !NT_SUCCESS(status)) goto exit; mFilter.action.calloutKey = *calloutKey; //在callout里決定 mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING; mFilter.displayData.name = L"WFP TEST"; mFilter.displayData.description = L"TESLA.ANGELA's WFP TEST"; mFilter.layerKey = *layerKey; mFilter.numFilterConditions = 0; mFilter.filterCondition = mFilter_condition; mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; mFilter.weight.type = FWP_EMPTY; //添加過濾器 status = FwpmFilterAdd( gEngineHandle,&mFilter,NULL,filterId ); if( !NT_SUCCESS( status)) goto exit; exit: if( !NT_SUCCESS(status)) { if( bCalloutRegistered ) { FwpsCalloutUnregisterById( *calloutId ); } } return status; } NTSTATUS WallRegisterCallouts() { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInTransaction = FALSE; BOOLEAN bEngineOpened = FALSE; FWPM_SESSION session = {0}; session.flags = FWPM_SESSION_FLAG_DYNAMIC; //開啟WFP引擎 status = FwpmEngineOpen( NULL,RPC_C_AUTHN_WINNT,NULL,&session,&gEngineHandle ); if( !NT_SUCCESS(status)) goto exit; bEngineOpened = TRUE; //確認過濾權限 status = FwpmTransactionBegin( gEngineHandle,0 ); if( !NT_SUCCESS(status)) goto exit; bInTransaction = TRUE; //注冊回調函數 status = RegisterCalloutForLayer(&FWPM_LAYER_ALE_AUTH_CONNECT_V4,&GUID_ALE_AUTH_CONNECT_CALLOUT_V4,WallALEConnectClassify,WallNotifyFn,WallFlowDeleteFn,&gAleConnectCalloutId,&gAleConnectFilterId); if( !NT_SUCCESS(status)) { DbgPrint("RegisterCalloutForLayer-FWPM_LAYER_ALE_AUTH_CONNECT_V4 failed!\n"); goto exit; } //確認所有內容并提交,讓回調函數正式發揮作用 status = FwpmTransactionCommit(gEngineHandle ); if( !NT_SUCCESS(status)) goto exit; bInTransaction = FALSE; exit: if( !NT_SUCCESS(status)) { if( bInTransaction) { FwpmTransactionAbort( gEngineHandle ); } if( bEngineOpened ) { FwpmEngineClose( gEngineHandle ); gEngineHandle = 0; } } return status; }注銷的代碼:
NTSTATUS WallUnRegisterCallouts() { if( gEngineHandle != 0 ) { //刪除FilterId FwpmFilterDeleteById( gEngineHandle,gAleConnectFilterId ); //刪除CalloutId FwpmCalloutDeleteById( gEngineHandle,gAleConnectCalloutId ); //清空FilterId gAleConnectFilterId = 0; //反注冊CalloutId FwpsCalloutUnregisterById( gAleConnectCalloutId ); //清空CalloutId gAleConnectCalloutId = 0; //關閉引擎 FwpmEngineClose( gEngineHandle ); gEngineHandle = 0; } return STATUS_SUCCESS; }三個回調函數以及其他:
#include <ntddk.h> #pragma warning(push) #pragma warning(disable:4201) // unnamed struct/union #pragma warning(disable:4995) #include <ndis.h> #include <fwpsk.h> #pragma warning(pop) #include <fwpmk.h> #include <limits.h> #include <ws2ipdef.h> #include <in6addr.h> #include <ip2string.h> #include <strsafe.h> #include <wdm.h>#define INITGUID #include <guiddef.h> #define bool BOOLEAN #define true TRUE #define false FALSE #define DEVICE_NAME L"\\Device\\MyDriver" #define DEVICE_DOSNAME L"\\DosDevices\\MyDriver" #define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ') #define kfree(_p) ExFreePool(_p)DEFINE_GUID // {6812FC83-7D3E-499a-A012-55E0D85F348B} ( GUID_ALE_AUTH_CONNECT_CALLOUT_V4, 0x6812fc83, 0x7d3e, 0x499a, 0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b );PDEVICE_OBJECT gDevObj;HANDLE gEngineHandle = 0; HANDLE gInjectHandle = 0; //CalloutId UINT32 gAleConnectCalloutId = 0; //FilterId UINT64 gAleConnectFilterId = 0;/* 以下兩個回調函數沒啥用 */ NTSTATUS NTAPI WallNotifyFn (IN FWPS_CALLOUT_NOTIFY_TYPE notifyType,IN const GUID *filterKey,IN const FWPS_FILTER *filter ) { return STATUS_SUCCESS; }VOID NTAPI WallFlowDeleteFn (IN UINT16 layerId,IN UINT32 calloutId,IN UINT64 flowContext ) { return; }//協議代碼轉為名稱 char* ProtocolIdToName(UINT16 id) { char *ProtocolName=kmalloc(16); switch(id) //http://www.ietf.org/rfc/rfc1700.txt { case 1: strcpy_s(ProtocolName,4+1,"ICMP"); break; case 2: strcpy_s(ProtocolName,4+1,"IGMP"); break; case 6: strcpy_s(ProtocolName,3+1,"TCP"); break; case 17: strcpy_s(ProtocolName,3+1,"UDP"); break; case 27: strcpy_s(ProtocolName,3+1,"RDP"); break; default: strcpy_s(ProtocolName,7+1,"UNKNOWN"); break; } return ProtocolName; }//最重要的過濾函數 //http://msdn.microsoft.com/en-us/library/windows/hardware/ff551238(v=vs.85).aspx void NTAPI WallALEConnectClassify (IN const FWPS_INCOMING_VALUES0* inFixedValues,IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,IN OUT void* layerData,IN const void* classifyContext,IN const FWPS_FILTER* filter,IN UINT64 flowContext,OUT FWPS_CLASSIFY_OUT* classifyOut ) { char *ProtocolName=NULL; DWORD LocalIp,RemoteIP; LocalIp=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; RemoteIP=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; ProtocolName=ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16); DbgPrint("[WFP]IRQL=%d;PID=%ld;Path=%S;Local=%u.%u.%u.%u:%d;Remote=%u.%u.%u.%u:%d;Protocol=%s\n",(USHORT)KeGetCurrentIrql(),(DWORD)(inMetaValues->processId),(PWCHAR)inMetaValues->processPath->data, //NULL,//(LocalIp>>24)&0xFF,(LocalIp>>16)&0xFF,(LocalIp>>8)&0xFF,LocalIp&0xFF,inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16,(RemoteIP>>24)&0xFF,(RemoteIP>>16)&0xFF,(RemoteIP>>8)&0xFF,RemoteIP&0xFF,inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16,ProtocolName); kfree(ProtocolName); classifyOut->actionType = FWP_ACTION_PERMIT;//允許連接//禁止IE聯網(設置“行動類型”為FWP_ACTION_BLOCK) // if(wcsstr((PWCHAR)inMetaValues->processPath->data,L"iexplore.exe")) // { // classifyOut->actionType = FWP_ACTION_BLOCK; // classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; // classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; // } return; } 執行結果,進行網絡監控,并且禁止ie訪問網絡:
剛剛看有一個哥們寫的也不錯,留在這方便以后用的時候一起查看:
http://blog.csdn.net/rodney443220/article/details/37653793
還有一點要注意。回調函數執行的時候IRQL不一定是0,還可能是2,看上面圖片輸出。
?
總結
以上是生活随笔為你收集整理的Win64 驱动内核编程-16.WFP网络监控驱动(防火墙)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win64 驱动内核编程-15.回调监控
- 下一篇: 12.PHP_PDO数据库抽象层