zhuyu19832007
驱动牛犊
驱动牛犊
  • 注册日期2007-01-21
  • 最后登录2008-04-18
  • 粉丝0
  • 关注0
  • 积分20分
  • 威望3点
  • 贡献值0点
  • 好评度2点
  • 原创分0分
  • 专家分0分
阅读:2026回复:4

用passthru发送自定义数据包时黑屏

楼主#
更多 发布于:2007-01-22 10:06
  大家好,我在开发中间层驱动时碰到一些麻烦,以前常常来论坛看资料,学到不少东西,昨晚总算注册了一个帐号,希望大家多多关照。
    事情是这样的,我想在中间层与应用程序交互以实现Ad-Hod协议栈的功能,微软在DDK里有一个模板--passthru,根据这个模板,我加入了NdisMRegisterDevice,这样通过应用程序便可以往驱动发送IRP信息,在这之前,我先定义了一个MySend函数,传入一个DeviceObject和
Irp,DeviceObject是驱动对象,Irp是获取的客户端信息。
    因为在passthru中会维护一个pAdaptList链表,程序会把最后初始化的pAdapt加载到pAdaptList头,现在假设我的机器只有一块网卡,那么通过"pAdapt = pAdaptList;"语句就可以获取唯一的通道,最开始都非常顺利,划空间,申请包,绑定Buffer什么的,就是一发包,就直接黑屏了。
    我怀疑两点,第一是关于获取的pAdapt在用户发送的时候便已经释放了,第二点是protocol->PtSendComplete()函数的处理上有什么问题,我想得很简单,当回调PtSendComplete的时候,我获取Packet->ProtocolReserved包(自行构造的包是不往ProtocolReserved写入信息的),如果为真,便由NdisDprFreePacket函数释放包,并由NdisMSendComplete函数返回Pkt(谁申请谁释放原则),如果不为真,便自行释放包内存以及包。
    不过我想应该不会是这里的问题,我在黑屏之前在目的机器写了一个函数,用以读取包的,不过却没能发现源机"临死"时发的信息,所以这个信息是没有发送出去的。
    下面是我所提到的改动后的源码,麻烦您帮我看看,有什么地方不对的。




    以下是我的MySend函数,获取DeviceObject和Irp函数。用以初始化一个包并发送的。
NTSTATUS
MySend(
    IN PDEVICE_OBJECT        DeviceObject,
    IN PIRP                    Irp
)
{
    PADAPT                    pAdapt;
    PNDIS_PACKET            MyPacket;
    PNDIS_BUFFER            PacketBuffer;
    PRSVD                    Rsvd;
    NDIS_PHYSICAL_ADDRESS    HighestAcceptableAddress;

    NTSTATUS                Status = STATUS_SUCCESS;
    PUCHAR                    pPacketContent;
    UINT                    uOffset;             //偏移量
    PIO_STACK_LOCATION        tempIrpStack;

    PCHAR                    IoBuffer;
    ULONG                    inputBufferLength;
    ULONG                    outputBufferLength;

    int                        ii = 0;
    int                        bufLength;

    tempIrpStack = IoGetCurrentIrpStackLocation(Irp);
    inputBufferLength  = tempIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength = tempIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    IoBuffer = Irp->AssociatedIrp.SystemBuffer;

    HighestAcceptableAddress.LowPart = -1;
    HighestAcceptableAddress.HighPart = -1;

//--------------------------测试传递过来的数据--------------------------
/*
    if(IoBuffer == NULL)
    {
        DbgPrint("接收到的Buffer的信息为空\n");
        DbgPrint("inputBufferLength的值为:%d\n", inputBufferLength);
    }
    else
    {
        DbgPrint("接收到的Buffer的信息为:%s\n", pPacketContent);
        DbgPrint("inputBufferLength的值为:%d\n", inputBufferLength);
    }
*/
//-----------------------------------------------------------------------
    Status = NdisAllocateMemory(&pPacketContent, 2000, 0, HighestAcceptableAddress);//分配内存
    if (Status != NDIS_STATUS_SUCCESS )
    {
        return NDIS_STATUS_FAILURE;
    }

    if(pPacketContent == NULL)
    {
        return NDIS_STATUS_FAILURE;
    }

    RtlZeroMemory(pPacketContent, 2000);

    //目标MAC地址
    for(uOffset = 0; uOffset<=5; uOffset++)
    {
        pPacketContent[uOffset] = 0xff;
    }

    //源MAC地址
    pPacketContent[6] = 0x52;
    pPacketContent[7] = 0x54;
    pPacketContent[8] = 0xAB;
    pPacketContent[9] = 0x16;
    pPacketContent[10] = 0xB2;
    pPacketContent[11] = 0x7A;

    //两位协议
    pPacketContent[12] = 0x08;
    pPacketContent[13] = 0x00;

    for(uOffset = 0; uOffset < inputBufferLength; uOffset++)
    {
        pPacketContent[uOffset+14] = IoBuffer[uOffset];
    }

//--------------------------------
/*
    DbgPrint("pPacketContent字符串的信息为: \n");    
    while(ii<100)
    {
        DbgPrint("%x\n", pPacketContent[ii]);
        ii++;
    }
    DbgPrint("pPacketContent内容输出完毕!");
*/
//--------------------------------

    pAdapt = pAdaptList;

    //ASSERT(pAdapt->pSecondaryAdapt);
    //pAdapt = pAdapt->pSecondaryAdapt;

    if(IsIMDeviceStateOn(pAdapt) == FALSE)
    {
        return NDIS_STATUS_FAILURE;
    }

    NdisAllocatePacket(&Status, &MyPacket, pAdapt->SendPacketPoolHandle);//在包池里分配一个包
    if (Status != NDIS_STATUS_SUCCESS)
    {
        return Status;
    }

    NdisAllocateBuffer(&Status, &PacketBuffer, pAdapt->SendPacketPoolHandle, pPacketContent, 2000);//分配buffer
    if (Status != NDIS_STATUS_SUCCESS)
    {
        DbgPrint("PacketBuffer分配失败!");
        return Status;
    }

    NdisChainBufferAtFront(MyPacket, PacketBuffer); //绑定Buffer到MyPacket

    MyPacket->Private.Head->Next=NULL;
    MyPacket->Private.Tail=NULL;
    NdisSetPacketFlags(MyPacket, NDIS_FLAGS_DONT_LOOPBACK);//自己定义的包

    //MyReceive(MyPacket);
    NdisSend(&Status, pAdapt->BindingHandle, MyPacket);//发送

    if (Status != NDIS_STATUS_PENDING)//返回pending的话自己分配的东西要在PtSendComplete里面释放
    {
        NdisUnchainBufferAtFront(MyPacket ,&PacketBuffer);
        NdisQueryBufferSafe(PacketBuffer,(PVOID *)&pPacketContent,&bufLength,32);
        
        NdisFreeBuffer(PacketBuffer);
        NdisFreeMemory(pPacketContent,2000,0);
        NdisDprFreePacket(MyPacket);
    }

    return Status;
}

    下面是我写的一个简易的读取包信息的函数
MyReceive(
    PNDIS_PACKET MyPacket
)
{
    int PacketSize;
    PUCHAR pPacketContent;
    PUCHAR pBuf;
    UINT BufLength;
    MDL * pNext;
    UINT i;
    int ii = 0;

    //-----------------------把数据包内容从Packet拷贝到pPacketContent----------------------
        NdisQueryPacket(
                        MyPacket,
                        NULL,
                        NULL,
                        NULL,
                        &PacketSize);
        NdisAllocateMemory(
                        &pPacketContent,
                        2000,
                        0,
                        HighestAcceptableMax);
        NdisZeroMemory(
                        pPacketContent,
                        2000);
        NdisQueryBufferSafe(
                        MyPacket->Private.Head,
                        &pBuf,
                        &BufLength,
                        32);
        NdisMoveMemory(
                        pPacketContent,
                        pBuf,
                        BufLength);
        i = BufLength;
        pNext = MyPacket->Private.Head;
        for(;;)
        {
            if(pNext == MyPacket->Private.Tail)
                break;
            pNext = pNext->Next; //指针后移
            if(pNext == NULL)
                break;
            NdisQueryBufferSafe(pNext,&pBuf,&BufLength,32);
            NdisMoveMemory(pPacketContent+i,pBuf,BufLength);
            i+=BufLength;
        }
        //数据拷贝完毕
//--------------------------------
        DbgPrint("准备输出数据包内容!\n");
        while(ii<50)
        {
            DbgPrint("%x", pPacketContent[ii]);
            ii++;
        }
        DbgPrint("\n数据包内容输出完毕!");
//--------------------------------
}



这是我的回调函数。
VOID
PtSendComplete(
    IN    NDIS_HANDLE            ProtocolBindingContext,
    IN  PNDIS_PACKET        Packet,
    IN  NDIS_STATUS            Status
    )
{
    PADAPT            pAdapt;
    PNDIS_PACKET    Pkt;
    PUCHAR            pPacketContent;
    PNDIS_BUFFER    PacketBuffer;
    PRSVD            Rsvd;
    UINT            bufLength;

    //
    // Returning the Send on the Primary, will point to itself if there is no bundle
    //
    pAdapt = (PADAPT)ProtocolBindingContext;
    pAdapt = pAdapt->pPrimaryAdapt;

    if(Packet->ProtocolReserved)
    {
        Rsvd =(PRSVD)(Packet->ProtocolReserved);
        Pkt = Rsvd->OriginalPkt;
        
        NdisIMCopySendCompletePerPacketInfo(Pkt, Packet);
        NdisDprFreePacket(Packet);
        NdisMSendComplete(pAdapt->MiniportHandle, Pkt, Status);
    }
    else
    {
        NdisUnchainBufferAtFront(Packet, &PacketBuffer);
        NdisQueryBufferSafe(PacketBuffer, (PVOID *)&pPacketContent, &bufLength, 32);

        NdisFreeBuffer(PacketBuffer);
        NdisFreeMemory(pPacketContent, 2000, 0);
        NdisDprFreePacket(Packet); //释放
    }
}
    这只是一个试验,在我的自定义数据包中,最开始是12个'F',然后是本机的MAC地址,再是我们从IRP中获取的内容,我只是想在局域网上把这个包NdisSend出去。
 
驱网无线,快乐无限
rangzh
驱动小牛
驱动小牛
  • 注册日期2005-04-24
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望150点
  • 贡献值0点
  • 好评度115点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2007-01-23 00:10
有几个明显的地方:

1. 你的MySend函数看起来应该是DispatchWrite或者DispatchDeviceControl中调用的,那么它所运行的IRQL==IRQL_PASSIVE_LEVEL,此时你分配包描述符:
packet = NdisAllocatePacket(...)
然而你在这个函数中释放这个包描述符时却调用NdisDprFreePacket,这应该是有问题的吧?NdisDprFreePacket应在IRQL_DISPATCH_LEVEL环境下调用

2. 构建包描述符时,
NdisChainBufferAtFront(MyPacket, PacketBuffer); //绑定Buffer到MyPacket

MyPacket->Private.Head->Next=NULL;
MyPacket->Private.Tail=NULL;

这样写是有问题的吧?你先把缓冲描述符链到头部,又将头指针和尾指针置NULL,等于没链,而且那个缓冲描述符从此悬空了。去掉后面两行或者颠倒顺序

3. 调用NdisSend之前,包状态、带外数据等应该设置一下吧?你可以看看协议驱动的例子,这个我不太确定是否一定需要初始化OOB等。

4. 打印数据时,while(ii<50),所有的包都有50字节长吗
zhuyu19832007
驱动牛犊
驱动牛犊
  • 注册日期2007-01-21
  • 最后登录2008-04-18
  • 粉丝0
  • 关注0
  • 积分20分
  • 威望3点
  • 贡献值0点
  • 好评度2点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-01-23 10:16
非常感谢楼上的大哥讲解,我是学驱动的新人呵,本来是学JAVA的,现在赶鸭子上架。

第4处输出数据,我只是想看一下以太网包头,就是前14位的数据有没有错;

第3处OOB可以不用设,包状态我还没试过,等试了贴出来;

第2处我想应该是这样的,每个包在Private中会维护一个单链表,链头为Head,链尾是Tail,他们通过->Next连接在一起,由于我们是自定义包,只有一个Memory和一处Buffer,所以通过NdisChainBufferAtFront将Buffer绑到MyPacket->Private.Head后,Private.Head->Next和MyPacket->Private.Tail直接指空应该是可以的;

第1处我还完全不了解,嘿嘿。
总之非常谢谢楼上给的宝贵意见。
驱网无线,快乐无限
rangzh
驱动小牛
驱动小牛
  • 注册日期2005-04-24
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望150点
  • 贡献值0点
  • 好评度115点
  • 原创分0分
  • 专家分0分
地板#
发布于:2007-01-25 00:13
第2处看错了。

最好的办法就是调试跟踪一下
waiwai88
驱动牛犊
驱动牛犊
  • 注册日期2006-04-26
  • 最后登录2008-01-06
  • 粉丝0
  • 关注0
  • 积分30分
  • 威望4点
  • 贡献值0点
  • 好评度3点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2007-02-11 10:37
//KSPIN_LOCK        GlobalArraySpinLock;
//KIRQL            GIrqL;

NDIS_HANDLE        GBufferPool = NULL;

VOID  OnSendDone(IN PNDIS_PACKET pPacket)  {
    PNDIS_BUFFER anNdisBufferP;
    PVOID aBufferP;
    UINT aBufferLen;
    DbgPrint("passthruEx.sys : OnSendDone called\n");
    //__asm int 3
    /* aquire lock, release only when send is complete */
//    KeAcquireSpinLock(&GlobalArraySpinLock, &GIrqL);
    NdisUnchainBufferAtFront( pPacket, &anNdisBufferP );
    if(anNdisBufferP) {
        NdisQueryBuffer( anNdisBufferP, &aBufferP,  &aBufferLen);
        if(aBufferP)
            NdisFreeMemory( aBufferP, aBufferLen, 0 );
        NdisFreeBuffer(anNdisBufferP);
    }
    NdisReinitializePacket(pPacket);
    NdisFreePacket(pPacket);
    /* release so we can send next.. */
//    KeReleaseSpinLock(&GlobalArraySpinLock, GIrqL);
    return;
}
BOOLEAN DestoryMySendPacket(){

    if(GBufferPool != NULL)
        NdisFreeBufferPool(GBufferPool);
    GBufferPool = NULL;
    
}

VOID SendRaw( NDIS_HANDLE hAdapter, NDIS_HANDLE hPacketPool, NDIS_HANDLE hBufferPool, char *c,  int len ) {
    NDIS_STATUS aStat;
    /* aquire lock, release only when send is complete */
//    KeAcquireSpinLock(&GlobalArraySpinLock, &GIrqL);
    if( c && hPacketPool && hBufferPool){
        PNDIS_PACKET aPacketP;
        NdisAllocatePacket( &aStat,  &aPacketP,  hPacketPool );
        if(NDIS_STATUS_SUCCESS == aStat)
        {
            PVOID aBufferP;
            PNDIS_BUFFER anNdisBufferP;
            NDIS_PHYSICAL_ADDRESS phyaddr     = {-1};
            NdisAllocateMemory( &aBufferP, len, 0, phyaddr );
            memcpy( aBufferP, (PVOID)c, len);
            NdisAllocateBuffer( &aStat,  &anNdisBufferP,  hBufferPool, aBufferP, len );
            if(NDIS_STATUS_SUCCESS == aStat)
            {
                NdisChainBufferAtBack(aPacketP, anNdisBufferP);    
                NdisSend( &aStat, hAdapter, aPacketP );
                if (aStat != NDIS_STATUS_PENDING )
                    OnSendDone( aPacketP );    
            }
            else
                DbgPrint("passthruEx.sys : error 0x%X NdisAllocateBuffer\n");
        }
        else
            DbgPrint("passthruEx.sys : error 0x%X NdisAllocatePacket\n");
    }
    /* release so we can send next.. */
//    KeReleaseSpinLock(&GlobalArraySpinLock, GIrqL);
}

不知道哪里有错误了 GBufferPool 在初始化时已经分配好了,softice装不上(装上也不会用)windbg不会用,不会调试程序,不会看dump文件,只能看点调试信息,驱动的一些基本概念也不大清楚,大家帮我看看吧。
'//' 后边的几行代码都去了不知道有用不?
不知道哪里有错误一发包就死机?
另外万一NdisSend返回NDIS_STATUS_PENDING由谁来执行OnSendDone()啊?
暂时就这几个问题了有了再问大家!
新年快乐!
游客

返回顶部