在使用wfp写一个关于流量监控的demo时,使用的是将auth_connect的pid信息绑定在inbound_transport进行流量统计的,其中在关联pid时,创建flowcontext,添加到flowcontext链表,关联flowcontext 在传输层通过参数获取flowcontext,使用flowcontext,最后系统调用delete回调,释放内存。但是在使用auth-connect获取的pid进行关联时,有时status返回值为:status-object-name-exists。显示已关联。最后在执行unload驱动卸载时,通过执行fwpsflowremove,将没有销毁的flowcontext进行销毁。此时会偶然出现dump(kernel-security-check-failure)0x139的dump。看了下dump原因,表示list-entry损坏(double remove)这里很奇怪,意思是删除两次吗?解了下dump是崩溃在removeentrylist这里。这是什么原因导致的呢?有时会出现有时不会
参考GPT和自己的思路:根据你的情况描述,我认为可能是在销毁流量统计的链表时没有处理好一个节点被多次删除的情况,导致出现了list-entry损坏的问题。在进行链表删除时,需要保证每个节点只被删除一次,否则就会出现这样的问题。建议你检查一下你的代码,特别是涉及链表删除的部分,看看是否存在这样的问题。你也可以参考一些相关的编程资料,了解链表的操作方法以及如何保证链表的安全删除。
可以借鉴下
#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,
(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;
}
// WFP 回调
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;
}
// 注册 WFP
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;
}
// 卸载 WFP
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;
}
参考GPT和自己的思路:根据你提供的情况,可能存在以下几种原因导致偶现的dump问题:
双重删除导致list-entry损坏。这种情况可能是因为在进行flowcontext链表操作时,多次重复销毁同一个flowcontext对象,导致对同一个list-entry进行了多次删除操作,从而损坏了链表的结构。
内存泄漏导致的dump。在进行flowcontext内存释放时,如果有部分flowcontext对象没有被正确地销毁,就有可能导致内存泄漏,最终引发dump问题。
程序代码中存在其他的bug。在进行wfp进程流量监控的开发过程中,还可能存在其他的程序编码问题或逻辑错误,这些问题也有可能导致偶现的dump问题。
为了解决这个问题,你可以尝试通过以下几个方面进行排查:
检查你的代码是否正确。对于数据结构链表的处理,需要确保应用程序正确地维护链表的结构,防止出现双重删除等问题。可以使用调试工具对代码进行单步调试,查看代码的执行情况,寻找可能出现问题的地方。
检查内存释放是否完全。在进行内存释放时,需要确保所有的内存块都被正确地释放,防止出现内存泄漏的问题。可以使用内存检测工具来检测代码的内存分配和释放情况。
检查系统调用API是否正确。在进行系统调用操作时,需要确保正确地调用各个API函数,避免出现参数传递错误或使用错误的API函数等问题。可以查看相关API的文档,了解应该如何正确地使用API函数。
希望以上信息能够帮助你解决问题,如果还有其他的疑问或需要更多的帮助,请随时与我联系。
以下答案由GPT-4.5Plus大模型(语音)与博主A努力中.......共同编写:
从您的描述,发生kernel dump的原因很有可能是flowcontext链表在删除操作时发生了双重删除(double remove),导致链表结构损坏而触发内核崩溃。
双重删除通常会在以下情况下发生:
对于上述情况,可以检查以下方面:
另外,在驱动卸载时调用FWPS_CALLOUT_CLASSIFY_FN0将所有流删除,这也增加了flowcontext双重删除的概率。可以考虑在此时禁止新流创建,稍后顺序删除并释放所有流,减少并发操作带来的链表损坏风险。
总之,需要检查流和flowcontext的创建删除操作的正确性,避免并发操作导致的链表结构损坏问题。这应该可以较大概率解决此dump问题。