围观网络之三 -- 浅探索NDIS5.1(2)

来源:互联网 发布:南方cass软件 编辑:程序博客网 时间:2024/05/29 18:02

二、       各个组件的绑定

1.       生成设备

 

当PNP管理器检测到有NIC的时候,会遍历所有注册的微端口驱动,通知他们要AddDevice ,从ndisRegisterMiniportDriver可以看到这个过程已经被NDIS托管

 

int __stdcall ndisAddDevice(int DriverObject, _UNICODE_STRING **SourceString, PDEVICE_OBJECTTargetDevice, char a4)

 

ndisAddDevice的大体逻辑是:

a, DriverBlock = IoGetDriverObjectExtension(DriverObject, 'NMID'); 得到驱动的 驱动块(DriverHandle) ,遍历ndisMiniDriverList 找到与之对应的节点

 

b, 创建一个设备对象,attach到\Device\PnPManager

其中 微端口设备块(_NDIS_MINIPORT_BLOCK)作为设备扩展的一部分miniPortBlock = (DeviceObject->DeviceExtension+ 72);

 

这是一个非常大的结构,在AddService中主要对这个结构一些初始化,需要注意的是以下几点

 

 *(DeviceObject->DeviceExtension + 20) = _DriverBlock; 设备扩展前面这个结构0x48的小结构没有搞清,但可以明确这里保存了驱动块 miniPortBlock->PrimaryMiniport =miniPortBlock; miniPortBlock->PhysicalDeviceObject =_TargetDevice;   //指向挂载的设备 miniPortBlock->DeviceObject = DeviceObject;                 //指向自己所属的驱动设备miniPortBlock->NextDeviceObject =TargetDevice; miniPortBlock->WrapperContext =DeviceObject->DeviceExtension;     指向设备扩展 miniPortBlock->NextGlobalMiniport = ndisMiniportList;挂入全局链表 ndisMiniportList = miniPortBlock;


 

2.协议帮顶下层真实微端口驱动

PART ONE 绑定初始化

 

在passthru!PtBindAdpter被调用时可以看到堆栈

 

Passthru!PtBindAdapterNDIS!ndisInitializeBindingNDIS!ndisCheckAdapterBindingsNDIS!ndisCheckProtocolBindingsNDIS!NdisReEnumerateProtocolBindingsPassthru!PtPnPNetEventReconfigurePassthru!PtPNPHandlerNDIS!ndisIMCheckDeviceInstanceNDIS!ndisPnPDispatchnt!IopfCallDrivernt!IopSynchronousCallnt!IopStartDevicent!PipProcessStartPhase1nt!PipProcessDevNodeTreent!PiRestartDevicent!PipDeviceActionWorkernt!ExpWorkerThreadnt!PspSystemThreadStartupnt!KiThreadStartup


 

PNP管理器检索一个驱动设备并调用nt!IopStartDevice 这里之前和WDM都一样

 

NDIS!ndisPnPDispatch托管函数托管了IRP_MJ_PNP 

 

IRP_MN_START_DEVICE的时候将执行以下代码


if (!Minor )   {    pMiniPortBlock->PnPFlags =&_NULL_IMPORT_DESCRIPTOR | pMiniPortBlock->PnPFlags & 0xFFFFFFEF;    IrpSp = v6->Tail.Overlay.___u4.CurrentStackLocation;memcpy(&IrpSp[-1], IrpSp, 0x1Cu);    *(&IrpSp->Control - 36) = 0;    v5 = ndisPassIrpDownTheStack(pIrp, v14);  //向设备栈下一层下发    if ( v5 >= 0 )    {      _DriverBlock =pMiniPortBlock->DriverHandle;      if ( _DriverBlock->Flags & 1 ) //这个标志表示这是一个中间层驱动的 驱动块      {        BYTE1(pMiniPortBlock->Flags) |=0x80u;        if (ndisIMCheckDeviceInstance(_DriverBlock, &pMiniPortBlock->MiniportName,&pDevObj) )       {         KeWaitForSingleObject(&pMiniPortBlock->DriverHandle->IMStartRemoveMutex,0, 0, 0, 0);          v5 = ndisIMInitializeDeviceInstance(pMiniPortBlock,pDevObj, 1);         KeReleaseMutex(&pMiniPortBlock->DriverHandle->IMStartRemoveMutex,0);        }      }      Else //否则这是一个miniport      {        if (ndisPnPStartDevice(pDevObj, pIrp)== 0 && BYTE2(pMiniPortBlock->Flags) & 2  &&  !ndisMediaTypeCl[pMiniPortBlock->MediaType]&& pMiniPortBlock->MediaType !=3)        {          RtlInitUnicodeString(&DestinationString,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NDProxy");          ZwLoadDriver(&DestinationString);          if ( ndisProtocolList )           ndisQueueBindWorkitem(pMiniPortBlock);        }      }    }    pIrp->IoStatus.___u0.Status = v5;    goto END;  }

由于是中间层驱动 所以下面调用了NDIS!ndisIMCheckDeviceInstance(_NDIS_M_DRIVER_BLOCK *pDriverBlock, PUNICODE_STRING deviceName, PDEVICE_OBJECT *ppDevObj)

      ndisIMCheckDeviceInstance首先将从pDriverBlock->PendingDeviceList找到与deviceName对应的设备(_NDIS_ PENDING _IM_INSTANCE结构) 并且存储到ppDevObj中去

      然后调用pDriverBlock->AssociatedProtocol->ProtocolCharacteristics.PnPEventHandler(v9,&netEvent)

 

V9是一个指针类型, 这个类型由驱动程序定义(ADAPT) 代表着协议驱动向下的一个绑定关系,如果这个值为0 代表这个Pnp是针对全局的,这里是绑定消息,当然是全局的…因为这个结构还没建立。。。

 

理所当然要到PtPnPNetEventReconfigure了,没有做什么处理

    if (pAdapt == NULL)

            NdisReEnumerateProtocolBindings(ProtHandle);  //这个是   _NDIS_PROTOCOL_BLOCK   

 

NdisReEnumerateProtocolBindings直接调用ndisCheckProtocolBindings 这个函数遍历ndisMiniDriverList的所有微端口驱动块,再遍历_DriverBlock->MiniportQueue——驱动块下的所有的微端口块,依次调用ndisCheckAdapterBindings,也就是所有的微端口驱动都要对这个协议进行绑定

 

ndisCheckAdapterBindings主要就是读mini设备的注册表参数 检查一下是否匹配 然后就调用ndisInitializeBinding,这是最后的初始化了


if (ndisIsMiniportStarted(mini) )  {    if (mini->PnPDeviceState != 1 )      jmp xxx    if ( ndisProtocolAlreadyBound(_protHandle,mini) != 1 ) //确认没有绑定    {      _protHandle->BindDeviceName =&mini->MiniportName; //绑定名      _protHandle->RootDeviceName = mini->BindPaths->Paths;                               //绑定路径      if (ndisReferenceRef(&_protHandle->Ref) )      {          //略去一些简单结构初始化和字符串的处理          if ( !_protHandle->Ref.Closing )          {            _protHandle->BindingAdapter = miniPortBlock           _protHandle->ProtocolCharacteristics.BindAdapterHandler(&v29,&v16, v5, &Destination, v13); 调用了我们的注册回调

PART TWO  下面就该真真正正的绑定了

现在到了passThru领空,调用界面如下

VOID

PtBindAdapter(

    OUTPNDIS_STATUS            Status,

   IN NDIS_HANDLE             BindContext,//这里又出现了一个句柄 这个是ADAPT

    IN  PNDIS_STRING            DeviceName,

    IN  PVOID                   SystemSpecific1,//PASSTHRU\Parameters\Adapters\NDISWANIP

    IN  PVOID                   SystemSpecific2//PDEVICE_OBJECT miniport设备

    )

 

开始初始化了ADAPT结构绑定上下文 定义了自己的上层mini和下层prot的相关通信结构

然后调用了这里

        NdisOpenAdapter(Status,

                          &Sts,

                          &pAdapt->BindingHandle,  //

                          &MediumIndex,

                          MediumArray,

                          sizeof(MediumArray)/sizeof(NDIS_MEDIUM),

                          ProtHandle,

                          pAdapt,

                          DeviceName,

                          0,

                          NULL);

 

这个函数初始化了_NDIS_OPEN_BLOCK 块存到pAdapt->BindingHandle 中,然后调用了ndisMOpenAdapter

 

ndisMOpenAdapter初始化了MAC_BLOCK 并且进一步初始化了open_block 从Mini和prot中取了参数进行填充

 

并且openBlock->MiniportNextOpen =MiniBlock->OpenQueue; mini和prot各自有一个队列维护了彼此的联系openBlock

 openBlock->ProtocolNextOpen = protBlock->OpenQueue;

 

最后,表示初始化完毕 可以通知mini段开始初始化了

        *Status= NdisIMInitializeDeviceInstanceEx(DriverHandle,

                                          &pAdapt->DeviceName,

                                           pAdapt);

 

PART THREE miniport的初始化


只有下层prot绑定真实miniport成功之后,上层的mini edge才会初始化

Passthru!MPInitialize

NDIS!ndisMInitializeAdapter

NDIS!ndisInitializeAdapter

NDIS!ndisPnPStartDevice

NDIS!ndisIMInitializeDeviceInstance

NDIS!NdisIMInitializeDeviceInstanceEx

Passthru!PtBindAdapter

……

 

ndisMInitializeAdapter(_NDIS_M_DRIVER_BLOCK*DriverBlock, _RTL_QUERY_REGISTRY_TABLE *QueryTab, LSA_UNICODE_STRING*ustrString, void *pAdpt)

 

这个函数是微端口设备初始化的主要函数,在这个函数中,进一步填充了MiniBlock的成员(主要是那些Handler)

 

接着ndisQueueMiniportOnDriver(miniBlock,v92) )// 插入driverHandle->MiniPortQueue 的链表中,再然后就调用了driverHandle->MiniportCharacteristics.InitializeHandler

 

MPInitialize,主要是初始化自定义结构ADATP 这个比较简单。

至此应该所有的结构都初始化完成了,这里没有提到的细节是Miniport是如何被上层的protocol绑定的,但意思差不多,是由pnp管理器发起的啦...

三 数据包的发送

Part One 数据包的解释

 

NDIS!_NDIS_PACKET

   +0x000 Private          : _NDIS_PACKET_PRIVATE

   +0x020 MiniportReserved : [8] Uchar  下面的保留位都是给协议驱动和小端口驱动自己用的

   +0x028 WrapperReserved  : [8] UChar

   +0x020 MiniportReservedEx : [12] UChar

   +0x02c WrapperReservedEx : [4] UChar

   +0x020 MacReserved      : [16] UChar

   +0x030 Reserved         : [2] Uint4B

   +0x038 ProtocolReserved : [1] Uchar

typedef struct_NDIS_PACKET_PRIVATE{    UINT                PhysicalCount;  //物理页面数    UINT                TotalLength;    // 总长度    PNDIS_BUFFER        Head;        mdl 链表头  基本上每个协议块占一个mdl    PNDIS_BUFFER        Tail;             PNDIS_PACKET_POOL   Pool;        所在的池?    UINT                Count;    ULONG               Flags;    BOOLEAN             ValidCounts;    UCHAR               NdisPacketFlags;    // SeefPACKET_xxx bits below    USHORT              NdisPacketOobOffset;//根据这个偏移可以获得OBB结构}NDIS_PACKET_PRIVATE, * PNDIS_PACKET_PRIVATE; typedefstruct_NDIS_PACKET_OOB_DATA{    union    {        ULONGLONG   TimeToSend;        ULONGLONG   TimeSent;    };    ULONGLONG       TimeReceived;    UINT            HeaderSize;    UINT            SizeMediaSpecificInfo;    PVOID           MediaSpecificInformation;    NDIS_STATUS     Status;}NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;

紧跟着OBB的是Extension


typedef struct_NDIS_PACKET_EXTENSION{     PVOID       NdisPacketInfo[MaxPerPacketInfo];}NDIS_PACKET_EXTENSION, *PNDIS_PACKET_EXTENSION;

Part Two 数据包的发送过程

在MPSend上下断点堆栈如下

Passthru!MPSendPackets

NDIS!ndisMSendX

psched!MpSend

NDIS!ndisMSendX

tcpip!ARPSendBCast

tcpip!ARPTransmit

tcpip!SendIPPacket

tcpip!SendDHCPPacket

tcpip!IPTransmit

tcpip!UDPSend

tcpip!TdiSendDatagram

tcpip!UDPSendDatagram

tcpip!TCPDispatchInternalDeviceControl

nt!IopfCallDriver

afd!AfdFastDatagramSend

 

看函数名这应该是一个ARP广播包,下发数据包的主要函数是ndisMSendX

 

我们平时调用的是NdisSend 这也是一层封装,它是这样一个宏


#define NdisSend(Status, NdisBindingHandle, Packet)                                     \{                                                                                      \    *(Status)=                                                                        \        (((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.SendHandler)(    \            ((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.BindingHandle, \        (Packet));                                                                     \}

NDIS_OPEN_BLOCK这两个变量设置如下,BindingHandle 就是openBlock本身 

   +0x030 SendHandler      : 0xf96f687b     int NDIS!ndisMSendX+0

   +0x054 SendPacketsHandler : 0xf970e25f     void NDIS!ndisMSendPacketsX+0

 

ndisMSendPacketsX做了一些处理,但最主要的是调用了openBlock->WSendPacketsHandler

 

Part Three   NIC网卡驱动的中断处理和数据包的接收


真正的小端口驱动需要注册中断,当接收到数据包时,将以中断的方式向上递交

 

看一下 接收handler的栈吧

Passthru!PtReceive

NDIS!ethFilterDprIndicateReceivePacket  这个函数是NDIS_MINIPORT_BLOCK.PacketIndicateHandler注册的

vmxnet+0x3458   网卡驱动注册的MiniportDpc

NDIS!ndisMDpcX中断的DPC函数

nt!KiRetireDpcList

nt!KiDispatchInterrupt

 

ndisMDpcX的参数是如下的结构

typedef struct_NDIS_MINIPORT_INTERRUPT{    PKINTERRUPT                 InterruptObject;    KSPIN_LOCK                  DpcCountLock;    PVOID                       Reserved;    W_ISR_HANDLER               MiniportIsr;    W_HANDLE_INTERRUPT_HANDLER  MiniportDpc;    KDPC                        InterruptDpc;    PNDIS_MINIPORT_BLOCK        Miniport;       UCHAR                       DpcCount;    BOOLEAN                     Filler1;    KEVENT                      DpcsCompletedEvent;    BOOLEAN                     SharedInterrupt;    BOOLEAN                     IsrRequested;}NDIS_MINIPORT_INTERRUPT, *PNDIS_MINIPORT_INTERRUPT;

中断函数是NDIS!ndisMIsr 这个函数会调用 NDIS_MINIPORT_INTERRUPT .ISRHandler(在这个中断中接收数据),然后插入dpc NDIS!ndisMDpcX,

ndisMDpcX会调用NDIS_MINIPORT_INTERRUPT.MiniportDpc,而在ethFilterDprIndicateReceivePacket中将最终遍历所有绑定了这个adapter的协议,向上发送数据包(调用_NDIS51_PROTOCOL_CHARACTERISTICS.ReceiveHandler)

 

BTW:在这个函数NDIS_MINIPORT_INTERRUPT.MiniportDpc执行的最后,会调用另外一个DPC处理函数,处理掉MINIPORT_BLOCK里面的workItem,这里面就包括了数据发送和信息查询等等

 

参考资料:

1. NDIS 5.1学习笔记

2. MSDN

3. IDA,Windbg,ROS src,NT4 src


原创粉丝点击