• NDIS IM 驱动那些事情


      最近不知道为什么开始学习windows NDIS驱动开发,用的是寒江独钓的例子,其实他的改的代码也就一点点,说的有用的东西也就那么多,不过还是感谢他出了这么一本书,不然这真的就没有一本稍微好一点的书籍来说windows 的NDIS书籍了。

      这方面的知识貌似大家都不太愿意说,我在网上找了好久无非就是那几篇经典的例子,不过网上代码有点错误有点问题,而且核心都不原因讲解。。

    比喻大家都会遇到问题,怎么在NDIS中间层发送自定义数据包的问题? 一般我们发送都会蓝屏,网上有类似的文章。

    http://blog.csdn.net/floweronwarmbed/article/details/3202065

    NDIS_STATUS
    MySendPacket (
       NDIS_HANDLE     NdisBindingHandle,
       NDIS_HANDLE     NdisSendPacketPool,
       PVOID           pBuffer,
       ULONG           dwBufferLength
       )
    {
       NDIS_STATUS     status;
       PNDIS_PACKET    pSendPacket = NULL;
       PNDIS_BUFFER    pSendPacketBuffer = NULL;
       PUCHAR          pSendBuffer = NULL;
       ULONG           dwSendBufferLength; 
       NDIS_PHYSICAL_ADDRESS HighestAcceptableAddress;
       PSEND_RSVD      SendRsvd = NULL;
    
       if (!NdisBindingHandle)
           return NDIS_STATUS_FAILURE;
    
       if (!pBuffer)
           return NDIS_STATUS_FAILURE;
    
       if (dwBufferLength > ETH_MAX_PACKET_SIZE)
           return NDIS_STATUS_FAILURE;
    
       HighestAcceptableAddress.QuadPart = -1;
       dwSendBufferLength = max(dwBufferLength, ETH_MIN_PACKET_SIZE);
    
       status = NdisAllocateMemory(&pSendBuffer, dwSendBufferLength, 0, HighestAcceptableAddress);
       if (status != NDIS_STATUS_SUCCESS)
       {
           return status;
       }
    
       RtlZeroMemory(pSendBuffer, dwSendBufferLength);
       RtlMoveMemory(pSendBuffer, pBuffer, dwSendBufferLength);
    
       NdisAllocatePacket(&status, &pSendPacket, NdisSendPacketPool);
       if (status != NDIS_STATUS_SUCCESS)
       {
           NdisFreeMemory(pSendBuffer, dwSendBufferLength, 0);
           
           return status;
       }
    
       NdisAllocateBuffer( &status, 
                           &pSendPacketBuffer, 
                           NdisSendPacketPool, 
                           pSendBuffer, 
                           dwSendBufferLength );
       if (status != NDIS_STATUS_SUCCESS)
       {
           NdisFreeMemory(pSendBuffer, dwSendBufferLength, 0);
           NdisDprFreePacket(pSendPacket);
    
           return status;
       }
    
       NdisChainBufferAtFront(pSendPacket, pSendPacketBuffer);
    
       SendRsvd = (PSEND_RSVD)(pSendPacket->ProtocolReserved); 
       SendRsvd->OriginalPkt = NULL; //注意这里
    
       pSendPacket->Private.Head->Next=NULL; 
       pSendPacket->Private.Tail=NULL; 
    
       //NDIS_SET_PACKET_HEADER_SIZE(pSendPacket, 14);
       NdisSetPacketFlags(pSendPacket, NDIS_FLAGS_DONT_LOOPBACK);
    
       NdisSend(&status, NdisBindingHandle, pSendPacket);
       if (status != STATUS_PENDING)
       {
           NdisUnchainBufferAtFront(pSendPacket ,&pSendPacketBuffer); 
           NdisQueryBufferSafe( pSendPacketBuffer, 
                                (PVOID *)&pSendBuffer, 
                                &dwSendBufferLength, 
                                HighPagePriority );
           NdisFreeBuffer(pSendPacketBuffer); 
           NdisFreeMemory(pSendBuffer, dwSendBufferLength, 0); 
           NdisDprFreePacket(pSendPacket);
       }
    
       return status;
    }
    
    
    注意:NdisSend如果是立刻完成,没有Pending的话,你需要在NdisSend返回后释放掉刚才分配的资源,否则是Pending的话,我们就要等发生包这个事件真正完成是的Complete例程里面去释放分配的资源。
    
    在函数PtSendComplete中:
    PSEND_RSVD        SendRsvd;
    SendRsvd = (PSEND_RSVD)(Packet->ProtocolReserved);
    Pkt = SendRsvd->OriginalPkt;
    // ProtocolReserved是个可以自己放自己数据的地方, passthru用这个存放原始包的地址, 而我们自己构造包的时候把SendRsvd->OriginalPkt设为了NULL,所以很容易判断出那个已完成发送的包是passtru的,哪些是我们构造的
    
           if (!Pkt )
           {
               NdisUnchainBufferAtFront(Packet, &pMySendPacketBuffer);
    
               if (pMySendPacketBuffer)
               {
                   NdisQueryBufferSafe( pMySendPacketBuffer, 
                                        (PVOID *)&pMySendBuffer, 
                                        &dwMySendBufferLength, 
                                        HighPagePriority ); 
                   if (pMySendBuffer && dwMySendBufferLength)
                   {
                       NdisFreeMemory(pMySendBuffer, dwMySendBufferLength, 0);
                   }
    
                   NdisFreeBuffer( pMySendPacketBuffer );
               }
    
                NdisDprFreePacket(Packet);

    但这里代码比较模糊也没有说明蓝屏的本质,只是PtSendComplete里面要释放原因。。。

    其实这里我测试得出,其实你释放不释放最多内存泄漏,比喻你的程序内存泄漏基本不会马上崩溃,应用程序崩溃一般是我们内存越界,类似系统不过是一个最大的应用程序。然后自己也在网上看到类似不要调用NdisMSendComplete类似的东西,这个API 通知上层驱动释放资源 等等问题,显然我NidsSend是在自己中间层调用根本没有上层,那么上层怎么释放呢!!显然就报错嘛?蓝屏还用说吗??我发现驱动蓝屏比较恐怖的事情,这2周学习感觉还好,只要你内存操作注意点时候,基本上不会蓝屏,内存泄漏感觉只会影响驱动稳定性。

      貌似上面的MySendPacket 里面那个

    NdisAllocateBuffer( &status, 
                           &pSendPacketBuffer, 
                           NdisSendPacketPool, 
                           pSendBuffer, 
                           dwSendBufferLength );
    这里应该是错误,
    NdisSendPacketPool 这个句柄明显是分配Packet。
    我看一下NdisAllocateBuffer 句柄应该是自己调用的VOID
    NdisAllocateBufferPool(
    OUT PNDIS_STATUS Status,
    OUT PNDIS_HANDLE PoolHandle,
    IN UINT NumberOfDescriptors
    );
    。。
    我不知道是不是他们估计要把代码写错,如果能混用的话感觉微软就没有必要提供NdisSendPacketPool的函数了。
    等我自己一个项目整体完成了,然后把代码分享出来。-----------》实现那种局域网管理软件使用ARP欺骗进行管理的核心功能。

    前几天看到一个厉害人吧,感觉能力比较强,看他的文章他非常熟悉linux的内核。然后开发起windows驱动非常顺手,看来linux真是c/c++的乐园吧。。。
    有时间还得仔细学习学习。



    最后我贴一下我写的ptComple的函数的API 其实很简单了。。
    VOID
    PtSendComplete(
        IN  NDIS_HANDLE            ProtocolBindingContext,
        IN  PNDIS_PACKET           Packet,
        IN  NDIS_STATUS            Status
        )
    /*++
    
    Routine Description:
    
        Called by NDIS when the miniport below had completed a send. We should
        complete the corresponding upper-edge send this represents.
    
    Arguments:
    
        ProtocolBindingContext - Points to ADAPT structure
        Packet - Low level packet being completed
        Status - status of send
    
    Return Value:
    
        None
    
    --*/
    {
        PADAPT            pAdapt = (PADAPT)ProtocolBindingContext;
        PNDIS_PACKET      Pkt; 
        NDIS_HANDLE       PoolHandle;
    
        PSEND_RSVD        tempRsvd;
        PNDIS_BUFFER      pMySendPacketBuffer = NULL;
        UINT              dwMySendBufferLength = 0;
        PVOID              pMySendBuffer = NULL;
    
        tempRsvd = (PSEND_RSVD)(Packet->ProtocolReserved);
        Pkt = tempRsvd->OriginalPkt;
    
        if (Pkt == (PNDIS_PACKET)1 )
        {
            NdisUnchainBufferAtFront(Packet, &pMySendPacketBuffer);
            if (pMySendPacketBuffer)
            {
                NdisQueryBufferSafe( pMySendPacketBuffer, 
                    (PVOID *)&pMySendBuffer, 
                    &dwMySendBufferLength, 
                    HighPagePriority ); 
                if (pMySendBuffer && dwMySendBufferLength)
                {
                    NdisFreeMemory(pMySendBuffer, dwMySendBufferLength, 0);
                }
    
                NdisFreeBuffer( pMySendPacketBuffer );
    
                NdisFreePacket(Packet);
            }
            return;
        }
    
    
    
    #ifdef NDIS51
        //
        // Packet stacking:
        //
        // Determine if the packet we are completing is the one we allocated. If so, then
        // get the original packet from the reserved area and completed it and free the
        // allocated packet. If this is the packet that was sent down to us, then just
        // complete it
        //
        //DbgPrint("我在NDIS51发送完了");
        PoolHandle = NdisGetPoolFromPacket(Packet);
        if (PoolHandle != pAdapt->SendPacketPoolHandle)
        {
            //
            // We had passed down a packet belonging to the protocol above us.
            //
            // DBGPRINT(("PtSendComp: Adapt %p, Stacked Packet %p
    ", pAdapt, Packet));
    
            
            NdisMSendComplete(pAdapt->MiniportHandle,
                              Packet,
                              Status);
        }
        else
    #endif // NDIS51
        {
    
            
            PSEND_RSVD        SendRsvd;
    
            SendRsvd = (PSEND_RSVD)(Packet->ProtocolReserved);
            Pkt = SendRsvd->OriginalPkt;
    
            //DbgPrint("我没有在NDIS51调用哦,熊熊你知道了蓝屏是为什么了");
        
    #ifndef WIN9X
            NdisIMCopySendCompletePerPacketInfo (Pkt, Packet);
    #endif
    
        
            NdisDprFreePacket(Packet);
    
            NdisMSendComplete(pAdapt->MiniportHandle,
                                     Pkt,
                                     Status);
        }
        //
        // Decrease the outstanding send count
        //
        ADAPT_DECR_PENDING_SENDS(pAdapt);
    }       
    点滴记录自己的成长,一步一步进步。 把写代码当成吃饭,我想我会成为牛魔王的。
  • 相关阅读:
    轻松实现 网络视频播放器
    端口扫描 多方式协同实现
    关于部分应用无法向POJ提交代码的解决方案
    一套跨平台五子棋网游的开发经历
    SSH2.0编程 ssh协议过程实现
    ssh秘钥交换详解与实现 diffie-hellman-group-exchange-sha
    OnlineJudge 离线题库采集
    SOCKET 实现NAT 穿越
    基于公网smtp协议实现邮件服务器
    周六搞事情,微信小程序开发文档已放出!
  • 原文地址:https://www.cnblogs.com/c-study/p/3682753.html
Copyright © 2020-2023  润新知