阅读:2027回复:4
用passthru发送自定义数据包时黑屏
大家好,我在开发中间层驱动时碰到一些麻烦,以前常常来论坛看资料,学到不少东西,昨晚总算注册了一个帐号,希望大家多多关照。
事情是这样的,我想在中间层与应用程序交互以实现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出去。 ![]() |
|
|
沙发#
发布于: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字节长吗 |
|
板凳#
发布于: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处我还完全不了解,嘿嘿。 总之非常谢谢楼上给的宝贵意见。 |
|
|
地板#
发布于:2007-01-25 00:13
第2处看错了。
最好的办法就是调试跟踪一下 |
|
地下室#
发布于: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()啊? 暂时就这几个问题了有了再问大家! 新年快乐! |
|