内核驱动发送TCP数据包:WFP/NDIS使用求助(C语言开发)
内核态发送TCP数据包的实现方案(针对WFP/NDIS使用困惑)
核心定位澄清
WFP并非专为主动发送数据包设计,它的核心能力是流量过滤、修改与注入;NDIS则是更底层的网络驱动接口,可直接构造并发送完整网络帧。根据你的需求,分两种场景给出实现思路:
方案1:WFP出站注入(适合已有TCP连接或依托现有连接发送数据)
如果目标IP:端口已有活跃TCP连接,或可通过用户态先建立连接再由内核注入数据,WFP是更简便的选择:
步骤拆解
- 初始化WFP引擎
调用FwpmEngineOpen0打开WFP过滤引擎,可选开启事务批量配置规则。 - 创建注入句柄
通过FwpsInjectionHandleCreate0创建注入句柄,指定注入模式(同步/异步)。 - 注册过滤规则
注册针对FWPM_LAYER_OUTBOUND_TRANSPORT_V4层的过滤规则,精确匹配目标地址192.168.1.105:32500,在过滤回调中触发注入逻辑。 - 构造并注入数据包
填充FWPS_TRANSPORT_SEND_PARAMS0结构体指定源/目标地址、端口,分配内核缓冲区存储待发送数据,调用FwpsInjectTransportSendAsync0完成注入。
简化代码示例
NTSTATUS InjectKernelTcpPacket(FWPS_INJECTION_HANDLE injectionHandle) { NTSTATUS status = STATUS_SUCCESS; FWPS_TRANSPORT_SEND_PARAMS0 sendParams = {0}; const CHAR sendData[] = "Kernel-sent TCP data"; const SIZE_T dataLen = sizeof(sendData); // 设置目标地址与端口(转换为网络字节序) sendParams.remoteAddressV4 = (IPV4_ADDRESS){.Byte = {192, 168, 1, 105}}; sendParams.remotePort = RtlUshortByteSwap(32500); sendParams.localPort = RtlUshortByteSwap(12345); // 自定义本地端口 // 分配非分页内存(内核态必须用此类内存分配函数) PVOID dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, dataLen, 'TCPK'); if (!dataBuffer) return STATUS_INSUFFICIENT_RESOURCES; RtlCopyMemory(dataBuffer, sendData, dataLen); // 执行注入 status = FwpsInjectTransportSendAsync0( injectionHandle, NULL, NULL, 0, AF_INET, &sendParams, NULL, dataBuffer, &dataLen, NULL // 同步模式下可忽略完成回调 ); ExFreePoolWithTag(dataBuffer, 'TCPK'); return status; }
- 资源清理
注入完成后,调用FwpsInjectionHandleDestroy0销毁句柄,FwpmEngineClose0关闭WFP引擎。
方案2:NDIS直接构造发送TCP帧(适合自定义完整TCP包,含握手)
如果需要主动发起TCP连接(构造SYN/SYN-ACK/ACK握手包)或完全自定义网络帧,需使用NDIS:
步骤拆解
- 枚举并打开目标网络适配器
枚举系统中能访问192.168.1.0/24段的网络适配器,调用NdisOpenAdapterEx打开适配器句柄。 - 构造完整网络帧
依次构造以太网帧头、IP头、TCP头与数据段:- 以太网帧:指定目的MAC(网关MAC)、源MAC(适配器MAC)、帧类型
0x0800(IP协议) - IP头:设置版本
4、协议6(TCP)、源/目标IP、计算IP校验和(用NdisComputeIpChecksum) - TCP头:设置源/目标端口、序列号、标志位(如SYN用于握手)、计算TCP校验和(用
NdisComputeTcpChecksum)
- 以太网帧:指定目的MAC(网关MAC)、源MAC(适配器MAC)、帧类型
- 发送数据包
将构造好的帧封装为NET_BUFFER_LIST,调用NdisSendNetBufferLists发送。
关键注意事项
- 驱动签名:Windows内核驱动必须签名,测试阶段可启用测试签名模式。
- 内存管理:内核态必须使用
ExAllocatePoolWithTag/ExFreePoolWithTag分配释放内存,优先选择NonPagedPoolNx避免执行漏洞。 - 字节序转换:所有端口、IP地址等多字节字段需转换为网络字节序(大端),使用
RtlUshortByteSwap/RtlUlongByteSwap。 - TCP握手:若需主动建立连接,需自行实现TCP三次握手逻辑,WFP无法直接完成此过程。
内容的提问来源于stack exchange,提问作者trapstar
相关产品推荐
相关产品推荐

