首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
1
1
[分享]封包拦截阶段实践(WFP)
发表于: 2026-3-13 15:46
1552
[分享]封包拦截阶段实践(WFP)
mb_binusgki
2026-3-13 15:46
1552
# 前言 之前学习了 Windows Filtering Platform 框架,今天实现一个对系统所有 TCP、UDP 请求的拦截监控,本次实践采用ALE_AUTH(授权阶段)确认请求,STREAM 层 (TCP 专用层)拦截TCP,DATAGRAM 层 (UDP 专用层)拦截UDP。 其中对于ALE_AUTH到STREAM和DATAGRAM的关联笔者使用了自制的Map通过flowHandle实现,并没有使用FwpsFlowAssociateContext进行回调(因为没有搞定),然后也并没有在一个Callout中设置多个Filter,而是直接设置每一层一个Callout 可以发现除了浏览器请求外系统通信也被成功拦截并成功获取封包内容,但目前还没有做封包解析  开启了浏览器后请求就刷屏了,非常多  # 注册Callout ``` NTSTATUS RegisterCallout( PDEVICE_OBJECT driver, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteFn, OUT ULONG32 *calloutId) { NTSTATUS status = STATUS_SUCCESS; // 初始化Callout FWPS_CALLOUT sCallout = { 0 }; sCallout.calloutKey = *calloutKey; sCallout.classifyFn = classifyFn; sCallout.flowDeleteFn = flowDeleteFn; sCallout.notifyFn = notifyFn; // 注册Callout status = FwpsCalloutRegister(driver, &sCallout, calloutId); return status; } ``` ### classifyFn ``` VOID NTAPI classifyFn_CONNECT( _In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut) ``` ### flowDeleteFn ``` VOID NTAPI flowDeleteFn( _In_ UINT16 layerId, _In_ UINT32 calloutId, _In_ UINT64 flowContext) ``` ### notifyFn ``` NTSTATUS NTAPI notifyFn( _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, _In_ const GUID* filterKey, _Inout_ FWPS_FILTER2* filter) ``` # 设置滤点 ``` NTSTATUS SetFilter( IN const GUID *layerKey, IN const GUID *calloutKey, OUT ULONG64 *filterId, OUT HANDLE *engine) { HANDLE hEngine = NULL; NTSTATUS status = STATUS_SUCCESS; FWPM_SESSION session = { 0 }; FWPM_FILTER mFilter = { 0 }; FWPM_CALLOUT mCallout = { 0 }; //创建Session session.flags = FWPM_SESSION_FLAG_DYNAMIC; status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &hEngine); if (!NT_SUCCESS(status)) { return status; } //开始事务 status = FwpmTransactionBegin(hEngine, 0); if (!NT_SUCCESS(status)) { return status; } //添加Callout FWPM_DISPLAY_DATA mDispData = { 0 }; mDispData.name = L"Test WFP"; mDispData.description = L"Testing"; mCallout.applicableLayer = *layerKey; mCallout.calloutKey = *calloutKey; mCallout.displayData = mDispData; status = FwpmCalloutAdd(hEngine, &mCallout, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } //设置过滤器参数 mFilter.action.calloutKey = *calloutKey; mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING; mFilter.displayData.name = L"MY WFP LyShark"; mFilter.displayData.description = L"WORLD OF DEMON"; mFilter.layerKey = *layerKey; mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; mFilter.weight.type = FWP_EMPTY;//如果 filter weight 太低可能被系统 filter 覆盖。 //添加过滤器 status = FwpmFilterAdd(hEngine, &mFilter, NULL, filterId); if (!NT_SUCCESS(status)) { return status; } //提交事务 status = FwpmTransactionCommit(hEngine); if (!NT_SUCCESS(status)) { return status; } //回写 engine 卸载用 *engine = hEngine; return status; } ``` # 初始化WFP ``` //初始化WFP NTSTATUS InitWfp(PDEVICE_OBJECT pDevObj) { PZY_PRINT("启动WFP"); NTSTATUS status = STATUS_SUCCESS; //初始化 Context Map MapInit(&g_Map); //CONNECT status = InitLayer(pDevObj, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, &GUID_CONNECT, classifyFn_CONNECT, notifyFn, flowDeleteFn, &g_keyinfos[CONNECT].CalloutId, &g_keyinfos[CONNECT].FilterId, & g_keyinfos[CONNECT].Engine); if (!NT_SUCCESS(status)) { PZY_PRINT("CONNECT回调失败"); return status; } //STREAM status = InitLayer(pDevObj, &FWPM_LAYER_STREAM_V4, &GUID_STREAM, classifyFn_STREAM, notifyFn, flowDeleteFn, &g_keyinfos[STREAM].CalloutId, &g_keyinfos[STREAM].FilterId, &g_keyinfos[STREAM].Engine); if (!NT_SUCCESS(status)) { PZY_PRINT("STREAM回调失败"); return status; } //DATAGRAM status = InitLayer(pDevObj, &FWPM_LAYER_DATAGRAM_DATA_V4, &GUID_DATAGRAM, classifyFn_DATAGRAM, notifyFn, flowDeleteFn, &g_keyinfos[STREAM].CalloutId, &g_keyinfos[STREAM].FilterId, &g_keyinfos[STREAM].Engine); if (!NT_SUCCESS(status)) { PZY_PRINT("DATAGRAM回调失败"); return status; } //开始循环检查Context Map LoopThroughContextMap(); return status; } ``` # classifyFn_CONNECT ``` VOID NTAPI classifyFn_CONNECT( _In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut) { // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0 WORD direct = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8; // 定义本机地址与本机端口 ULONG localIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; WORD localPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16; // 定义对端地址与对端端口 ULONG remoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; WORD remotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16; // 获取进程ID ULONG64 processId = inMetaValues->processId; // 获取当前协议类型 CHAR protocol[256] = { 0 }; GetProtocolNameByProtocolId(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, protocol); // 设置默认规则 允许连接 classifyOut->actionType = FWP_ACTION_PERMIT; //匹配key UINT64 transportEndpointHandle = inMetaValues->transportEndpointHandle; UINT64 flowHandle = inMetaValues->flowHandle; //储存 WFP_CONTEXT* wfpContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(WFP_CONTEXT), 'flow'); RtlZeroMemory(wfpContext, sizeof(WFP_CONTEXT)); wfpContext->transportEndpointHandle = transportEndpointHandle; wfpContext->flowHandle = flowHandle; wfpContext->processId = processId; wfpContext->protocol = protocol; wfpContext->direct = direct; wfpContext->localIp = localIp; wfpContext->localPort = localPort; wfpContext->remoteIp = remoteIp; wfpContext->remotePort = remotePort; MapPut(&g_Map, flowHandle, wfpContext); //输出 PZY_PRINT("[CONNECT][%llX][Pid:%d][Way:%s] Local[%u.%u.%u.%u:%d] %s Remote[%u.%u.%u.%u:%d]", flowHandle, processId, protocol, (localIp >> 24) & 0xFF, (localIp >> 16) & 0xFF, (localIp >> 8) & 0xFF, (localIp) & 0xFF, localPort, direct == FWP_DIRECTION_OUTBOUND ? "->" : "<-", (remoteIp >> 24) & 0xFF, (remoteIp >> 16) & 0xFF, (remoteIp >> 8) & 0xFF, (remoteIp) & 0xFF, remotePort ); } ``` # classifyFn_STREAM ``` VOID NTAPI classifyFn_STREAM( _In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut) { //layerData为空直接返回 if (layerData == NULL) return; //放行数据包, 如果你不设置可能会导致数据包被默认处理 //FWP_ACTION_PERMIT 允许 //FWP_ACTION_BLOCK 丢弃 //FWP_ACTION_CALLOUT_TERMINATING 终止并由 callout 决定 classifyOut->actionType = FWP_ACTION_PERMIT; //匹配key UINT64 transportEndpointHandle = inMetaValues->transportEndpointHandle; UINT64 flowHandle = inMetaValues->flowHandle; WFP_CONTEXT* wfpContext = MapGet(&g_Map, flowHandle); if (!wfpContext) { //匹配失败跳过 return; } //获取方向 WORD direct = wfpContext->direct; // 获取packet FWPS_STREAM_CALLOUT_IO_PACKET0* ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET0*)layerData; //获取stream FWPS_STREAM_DATA0* streamData = ioPacket->streamData; if (!streamData) { PZY_PRINT("streamData为空,返回"); return; } // 数据长度 UINT32 length = streamData->dataLength; if (length == 0) { PZY_PRINT("数据长度为0,返回"); return; } // 为 payload 分配缓冲区 PUCHAR buffer = ExAllocatePoolWithTag( NonPagedPool, length, STREAM_CALLOUT_TAG ); if (!buffer) { PZY_PRINT("分配缓冲区失败,返回"); return; } // 将 STREAM 数据复制到 buffer UINT32 copied = 0; FwpsCopyStreamDataToBuffer( streamData, buffer, length, &copied ); //打印 if (copied > 0) { // 最多打印 64 字节 UINT32 printLen = copied > 64 ? 64 : copied; // 每个字节 "XX " = 3 字符,再预留一些头部空间 SIZE_T outSize = printLen * 3 + 64; //分配内存 CHAR* outBuf = ExAllocatePoolWithTag( NonPagedPool, outSize, OUT_BUF_TAG ); if (!outBuf) return; //初始化内存 RtlZeroMemory(outBuf, outSize); // 当前写入位置 CHAR* cur = outBuf; // 剩余空间 SIZE_T remain = outSize; //状态 NTSTATUS status; // 写入头部 status = RtlStringCbPrintfExA( cur, remain, &cur, // 返回新的写入位置 &remain, // 返回剩余空间 0, "TCP Payload (%u bytes): ", copied ); //失败退出 if (!NT_SUCCESS(status)) goto EXIT; // 追加 HEX 数据 for (UINT32 i = 0; i < printLen; i++) { status = RtlStringCbPrintfExA( cur, remain, &cur, &remain, 0, "%02X ", buffer[i] ); if (!NT_SUCCESS(status)) break; } // 一次性输出 PZY_PRINT("[STREAM][%llX][%s] %s", flowHandle, direct == FWP_DIRECTION_OUTBOUND ? "发送数据" : "接收数据", outBuf); EXIT: ExFreePoolWithTag(outBuf, OUT_BUF_TAG); } //释放内存 ExFreePoolWithTag(buffer, STREAM_CALLOUT_TAG); //处理后删除map条目 MapRemove(&g_Map, flowHandle); } ``` # classifyFn_DATAGRAM ``` VOID NTAPI classifyFn_DATAGRAM( _In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut) { //layerData为空直接返回 if (layerData == NULL) return; //放行数据包, 如果你不设置可能会导致数据包被默认处理 //FWP_ACTION_PERMIT 允许 //FWP_ACTION_BLOCK 丢弃 //FWP_ACTION_CALLOUT_TERMINATING 终止并由 callout 决定 classifyOut->actionType = FWP_ACTION_PERMIT; //匹配key UINT64 flowHandle = inMetaValues->flowHandle; WFP_CONTEXT* wfpContext = MapGet(&g_Map, flowHandle); if (!wfpContext) { //匹配失败跳过 return; } //获取方向 WORD direct = wfpContext->direct; // DATAGRAM 层 layerData 是 NET_BUFFER_LIST NET_BUFFER_LIST* nbl = (NET_BUFFER_LIST*)layerData; NET_BUFFER* nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (!nb) return; ULONG length = NET_BUFFER_DATA_LENGTH(nb); if (length == 0) { PZY_PRINT("数据长度为0,返回"); return; } // 尝试直接获取数据指针 PUCHAR buffer = NdisGetDataBuffer( nb, length, NULL, 1, 0 ); PUCHAR localBuf = NULL; // 如果返回 NULL 说明数据跨 MDL if (buffer == NULL) { localBuf = ExAllocatePoolWithTag( NonPagedPool, length, DATAGRAM_CALLOUT_TAG ); if (!localBuf) return; buffer = NdisGetDataBuffer( nb, length, localBuf, 1, 0 ); } if (buffer) { // 最多打印 64 字节 ULONG printLen = length > 64 ? 64 : length; // 每个字节 "XX " = 3 字符,再预留一些头部空间 SIZE_T outSize = printLen * 3 + 64; //分配内存 CHAR* outBuf = ExAllocatePoolWithTag( NonPagedPool, outSize, OUT_BUF_TAG ); if (!outBuf) return; //初始化内存 RtlZeroMemory(outBuf, outSize); // 当前写入位置 CHAR* cur = outBuf; // 剩余空间 SIZE_T remain = outSize; //状态 NTSTATUS status; // 写入头部 status = RtlStringCbPrintfExA( cur, remain, &cur, // 返回新的写入位置 &remain, // 返回剩余空间 0, "UDP Packet (%lu bytes): ", length ); //失败退出 if (!NT_SUCCESS(status)) goto EXIT; // 追加 HEX 数据 for (UINT32 i = 0; i < printLen; i++) { status = RtlStringCbPrintfExA( cur, remain, &cur, &remain, 0, "%02X ", buffer[i] ); if (!NT_SUCCESS(status)) break; } // 一次性输出 PZY_PRINT("[DATAGRAM][%llX][%s] %s", flowHandle, direct == FWP_DIRECTION_OUTBOUND ? "发送数据":"接收数据", outBuf); EXIT: ExFreePoolWithTag(outBuf, OUT_BUF_TAG); } //释放内存 if (localBuf) ExFreePoolWithTag(localBuf, DATAGRAM_CALLOUT_TAG); //处理后删除map条目 MapRemove(&g_Map, flowHandle); } ``` # 总结 本贴为记录笔者实践过程,后续会继续完善封包解析功能...
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2026-3-13 19:32 被mb_binusgki编辑 ,原因: 补充内容
#调试逆向
#系统底层
#问题讨论
#其他内容
收藏
・
1
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
我的小拇指啊
为你点赞!
2026-3-17 15:53
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_binusgki
29
发帖
6
回帖
0
RANK
关注
私信
他的文章
[分享]注册表回调内核通信
379
[分享]简单编译器实现
1869
[分享]Lexer简单词法解析器入门
1233
[分享]vm解释器入门2
1288
[分享]vm解释器入门
1587
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部