中间层驱动下面连接ndis miniport驱动,上面连接ndis protocol驱动 ,可以监控所有本机收到的和发出去的网络包。自己写一个NDIS Intermediate Driver (中间层驱动) 可以过滤网络包实现类似防火墙的功能(我不知道那些防火墙是不是这样做的,好像都是去写NDIS Hook Drivers,而不是这个),也可以修改网络包,可以用来做VPN等功能(实际VPN客户端可能不一定是这样做的,不过确实有人写了类似的实例)。
有上图可以知道中间层是包括protocol和虚拟miniport两部分的,因为我自己做了一个arp防欺骗工具,所以像写一个这样的驱动来截取本机收到的ARP包。因为前面采用ndis protocol驱动来获取arp包也是可以的,但同时在发送ARP包的时候,可能是handle有冲突,我自己弄不好同步问题,索性再写个中间层驱动算了。参考WinDDK自带的 Passthru例子,稍加修改就可以达到目的。我想捕获本机收到的ARP包,所以只要修改protocol.c 中的PtReceivePacket()函数就可以了,本机收到网络包的时候系统是会自动回调这个函数的,我只要在这里过滤ARP包然后转发给自己的应用程序就行了。不过好像说是底层有的miniport不支持直接提交packet上来,所以也有可能是通过PtReceive()这个函数提交数据。所以也要改一下这个函数才行。我实际测试的时候也是有时是在PtReceivePacket()中得到数据,有时是在PtReceive()得到数据的。所以最后两个都改一下吧。如果像过滤本机发出去的网络包,那么看miniport那部分就行了。不过我没有做了,我只是想得到本机收到的ARP包而已了。
改后的PtReceivePacket()函数如下,其他的修改如PtReceive,还有就是和用户态应用程序交互IO控制相应那些,ARP包结构等就不细说了。
INT
PtReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
ReceivePacket handler. Called by NDIS if the miniport below supports
NDIS 4.0 style receives. Re-package the buffer chain in a new packet
and indicate the new packet to protocols above us. Any context for
packets indicated up must be kept in the MiniportReserved field.
NDIS 5.1 - packet stacking - if there is sufficient "stack space" in
the packet passed to us, we can use the same packet in a receive
indication.
Arguments:
ProtocolBindingContext - Pointer to our adapter structure.
Packet - Pointer to the packet
Return Value:
== 0 -> We are done with the packet
!= 0 -> We will keep the packet and call NdisReturnPackets() this
many times when done.
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
NDIS_STATUS Status;
PNDIS_PACKET MyPacket;
BOOLEAN Remaining;
//----------------------
ULONG readPacketBufferLength;
//-----------------------
//
// Drop the packet silently if the upper miniport edge isn't initialized or
// the miniport edge is in low power state
//
if ((!pAdapt->MiniportHandle) || (pAdapt->MPDeviceState > NdisDeviceStateD0))
{
return 0;
}
///-----widebright增加的 过滤 ARP包的 段----------------------------------------------------------------------------
DBGPRINT(("PtReceivePacket Function /n")); //
GetPktPayload(Packet, // Copy payload
&recARPPacket, // to area.
sizeof(recARPPacket), // Amount of space in area.
&readPacketBufferLength // Return number of bytes in packet.
);
if (readPacketBufferLength !=0)
{
if (recARPPacket.ehhdr.eh_type == 1544) // #define EPT_ARP 0x0806 1544= htons(EPT_ARP) 就是看是不是ARP包
{
DBGPRINT(("PtReceivePacket Function and ARP packet /n")); //
if (pEvent)
KeSetEvent(pEvent, 0, 0); //通知应用程序收到ARP 包了
}
}
//如果return 0 则表示丢弃 包,不传给上一层
///-----widebright增加的 过滤 ARP包的 段----------------------------------------------------------------------------
#ifdef NDIS51
//
// Check if we can reuse the same packet for indicating up.
// See also: PtReceive().
//
(VOID)NdisIMGetCurrentPacketStack(Packet, &Remaining);
if (Remaining)
{
//
// We can reuse "Packet". Indicate it up and be done with it.
//
Status = NDIS_GET_PACKET_STATUS(Packet);
NdisMIndicateReceivePacket(pAdapt->MiniportHandle, &Packet, 1);
return((Status != NDIS_STATUS_RESOURCES) ? 1 : 0);
}
#endif // NDIS51
//
// Get a packet off the pool and indicate that up
//
NdisDprAllocatePacket(&Status,
&MyPacket,
pAdapt->RecvPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
PRECV_RSVD RecvRsvd;
RecvRsvd = (PRECV_RSVD)(MyPacket->MiniportReserved);
RecvRsvd->OriginalPkt = Packet;
NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet);
NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet);
//
// Get the original packet (it could be the same packet as the one
// received or a different one based on the number of layered miniports
// below) and set it on the indicated packet so the OOB data is visible
// correctly to protocols above us.
//
NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet));
//
// Set Packet Flags
//
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
Status = NDIS_GET_PACKET_STATUS(Packet);
NDIS_SET_PACKET_STATUS(MyPacket, Status);
NDIS_SET_PACKET_HEADER_SIZE(MyPacket, NDIS_GET_PACKET_HEADER_SIZE(Packet));
if (pAdapt->MiniportHandle != NULL)
{
NdisMIndicateReceivePacket(pAdapt->MiniportHandle, &MyPacket, 1);
}
//
// Check if we had indicated up the packet with NDIS_STATUS_RESOURCES
// NOTE -- do not use NDIS_GET_PACKET_STATUS(MyPacket) for this since
// it might have changed! Use the value saved in the local variable.
//
if (Status == NDIS_STATUS_RESOURCES)
{
//
// Our ReturnPackets handler will not be called for this packet.
// We should reclaim it right here.
//
NdisDprFreePacket(MyPacket);
}
return((Status != NDIS_STATUS_RESOURCES) ? 1 : 0);
}
else
{
//
// We are out of packets. Silently drop it.
//
return(0);
}
}
可以看到我增加的代码是很少的, GetPktPayload是一个从别人的代码里面抄过来的,从packet结构读取包缓存的函数。一并转贴出来吧,如下:
/**************************************************************************************************/
/* */
/* Copy the payload of the specified packet into the specified buffer. */
/* */
/* Adapted from http://www.ndis.com/papers/ndispacket/readonpacket.htm, 12 May 2003. */
/* */
/**************************************************************************************************/
VOID
GetPktPayload(
PNDIS_PACKET pPacket, // Address of packet descriptor.
PUCHAR pOutBfr, // Address of output buffer, to get copied packet payload.
ULONG ulOutBfrAvail, // Size of output buffer.
PULONG pUlBytesCopied // Output variable for number of bytes copied.
)
{
PNDIS_BUFFER pNdisBfr;
ULONG ulBfrCnt,
ulTotPktLen,
ulCurrBfr,
ulAmtToMove;
PUCHAR pCurrBfr;
*pUlBytesCopied = 0; // Set 0 bytes copied.
if (0==ulOutBfrAvail) // Is output buffer 0 bytes in length?
goto Done;
NdisQueryPacket(pPacket, // Get information from packet descriptor.
NULL,
NULL,
&pNdisBfr, // Output variable for address of first buffer descriptor.
&ulTotPktLen // Output variable for number of bytes in packet payload.
);
NdisQueryBuffer(pNdisBfr, // Get information from first buffer descriptor.
&pCurrBfr, // Output variable for address of described virtual area.
&ulCurrBfr // Output variable for size of virtual area.
);
while (ulOutBfrAvail>0) // Space remaining in output buffer?
{
while (0==ulCurrBfr) // While the current buffer has zero length.
{
NdisGetNextBuffer(pNdisBfr, // Get next buffer descriptor.
&pNdisBfr
);
if (NULL==pNdisBfr) // None?
goto Done;
NdisQueryBuffer(pNdisBfr, // Get information from next buffer descriptor.
&pCurrBfr, // Output variable for address of current buffer.
&ulCurrBfr // Output variable for size of current buffer.
);
}
if (ulCurrBfr>ulOutBfrAvail) // Does current buffer's usable size exceed space remaining in output buffer?
ulAmtToMove = ulOutBfrAvail; // Use only amount remaining in output buffer.
else
ulAmtToMove = ulCurrBfr; // Use full size of current buffer.
NdisMoveMemory(pOutBfr, // Copy packet data to output buffer.
pCurrBfr,
ulAmtToMove
);
*pUlBytesCopied += ulAmtToMove; // Update output variable of bytes copied.
pOutBfr += ulAmtToMove; // Update pointer to output buffer.
ulOutBfrAvail -= ulAmtToMove; // Update number of bytes available in output buffer.
ulCurrBfr = 0; // Force search for next buffer.
} // End 'while' copy bytes to output buffer.
Done:
;
}
这就是获取ARP 包的关键代码了。如果像过滤 TCP/IP包其实也是要修改一点点就行了。
要什么其他的功能自己动手吧,我也是看了一下MSDN的参考而已,不甚明白。
自己动手丰衣足食!