围观网络之三 -- 浅探索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
- 围观网络之三 -- 浅探索NDIS5.1(2)
- 围观网络之三 -- 浅探索NDIS5.1(1)
- 围观网络之二 —— TDI~
- 视频:“360腾讯之歌”网络爆红 惹围观
- 深度围观block:三
- Groovy探索之Builder 三
- Groovy探索之MOP 十三 Interceptor 三(2)
- Groovy探索之MOP 十三 Interceptor 三(1)
- 网络围观心理分析 30W人围观跳楼
- Android探索之HttpURLConnection网络请求
- MIDP2.0仕样探索之三
- Groovy探索之闭包 三
- Groovy探索之Delegate模式 三
- 探索实践之软件构建(三)
- C++学习之三、探索继承技术
- 操作系统理论的探索: (之三)
- 【STM32F030探索套件】序列之三 Systick
- Groovy探索之闭包 三
- poj 3070 Fibonacci(矩阵乘法快速幂)
- android工程版key(复制自网络)
- MySQL5压缩版安装配置
- 判断android设备(如手机)是否已经ROOT的方法
- 第十六周实验报告 任务三(改)
- 围观网络之三 -- 浅探索NDIS5.1(2)
- 常见栈、队列面试题
- 获取系统相关信息的方法
- android 线程和进程的讲解
- eclipse中远程调试android普通应用和原生应用
- 50个必备的实用jQuery代码段
- IRQ probe failed (0xfffffff8) on Oracle VM virtualBOX
- 高考加油
- 编写服务端程序的要点