深度剖析WinPcap之(八)――打开与关闭适配器(17)

1.6      NPF中对应的函数接口

1.6.1        关键结构体 _OPEN_INSTANCE

结构体 _OPEN_INSTANCE包含NPF驱动程序一个运行实例的状态;这是NPF最重要的结构体:它被几乎所有驱动程序的函数使用;一个_OPEN_INSTANCE结构体与每个用户层会话关联,并允许对该驱动程序的并发访问。
结构体的具体定义如下:
typedef struct _OPEN_INSTANCE
{
    PDEVICE_EXTENSION   DeviceExtension;    // 指向该实例所缚设备的_DEVICE_EXTENSION结构体的指针
    NDIS_HANDLE         AdapterHandle;      // 该实例使用适配器的NDIS标识符
    UINT                Medium;             // 底层NDIS驱动使用的物理媒体类型。
// 参见微软WDKNdisOpenAdapter()函数的文档了解详情。
    NDIS_HANDLE         PacketPool;         //NDIS_PACKET 结构体的存储池,
// 用来与NIC驱动传输数据包。
    KSPIN_LOCK         RequestSpinLock;    // 用来同步OID请求的自旋锁
    LIST_ENTRY          RequestList;        // 挂起的OID请求列表
    LIST_ENTRY          ResetIrpList;       // 挂起的适配器复位请求列表
    INTERNAL_REQUEST    Requests[MAX_REQUESTS]; // 封装每单个OID请求的结构体数组
    PMDL                BufferMdl;          // 指向一个内存描述列表(MDL)的指针,
//MDL 映射了环形缓冲区的内存
    PKEVENT             ReadEvent;      // 指向事件的指针,在该事件上对该实例的读调用必须等待   
    PUCHAR              bpfprogram;     // 指向与当前实例相关的过滤伪代码的指针
                                        // 该代码只能在特定情景中使用(例如,当从NIC驱动程序接收的数据包存储在两个非连续的缓冲区中。)在正常情况下,使用被JIT编译器创建并被下一个字段(Filter)所指向的 过滤例程。参见NPF了解过滤过程的细节。
#ifdef _X86_
    JIT_BPF_Filter      *Filter;            // 指向由jitter所创建的本机过滤函数的指针。参见BPF_jitter()函数了解细节。
#endif //_X86_
    UINT                MinToCopy;          // 环形缓冲区中没被锁定的最小可读的数据数量,通过IOCTLBIOCSMINTOCOPY控制码设置
    LARGE_INTEGER       TimeOut;            // 读操作的超时值,即使缓冲区的数据数量低于MinToCopy(通过IOCTL控制码 BIOCSRTIMEOUT设置),读操作也返回。 int                   mode;               // 驱动程序的工作模式。参见PacketSetMode()函数了解细节
    LARGE_INTEGER       Nbytes;             // 当该实例在统计模式下时,记录被过滤器接收的字节数量。
    LARGE_INTEGER       Npackets;           // 当该实例在统计模式下时,记录被过滤器接收的数据包数量。
    NDIS_SPIN_LOCK      CountersLock;       // 保护统计模式计数器的自旋锁
    UINT                Nwrites;            // 一个数据包需要被重复发送的次数。参见NPF了解细节
    ULONG               Multiple_Write_Counter; // 对一个单个写操作已做物理重复的次数进行计数
    NDIS_EVENT          WriteEvent;     // 同步多个写进程的事件
    BOOLEAN             WriteInProgress;    // 如果正在写的过程中为TRUENPF当前允许在同一个open实例上执行一个单独的写操作
    NDIS_SPIN_LOCK      WriteLock;      // 保护WriteInProgress 变量的自旋锁
    NDIS_EVENT          NdisRequestEvent;   // 用来同步NDIS的回调结构体与I/O请求的事件
    BOOLEAN             SkipSentPackets;    // 如果该实例不应该捕获它自己所传输的数据包,则为True
    NDIS_STATUS         IOStatus;           // 维护OID请求调用的状态 ,这将传递给应用程序
    HANDLE              DumpFileHandle;     // 在转储模式下使用的文件句柄
    PFILE_OBJECT        DumpFileObject;     // 指向转储模式下使用的文件对象的指针
    PKTHREAD            DumpThreadObject;   // 指向转储模式下使用的线程对象的指针
    HANDLE              DumpThreadHandle;   // 转储模式下创建的线程句柄,为了异步地把数据从缓冲区搬移到磁盘
    NDIS_EVENT          DumpEvent;          // 转储模式下,用来同步转储线程与探针(tap)读取的事件
    LARGE_INTEGER       DumpOffset;         // 转储文件当前的偏移
    UNICODE_STRING      DumpFileName;       // 转储文件的文件名
    UINT                MaxDumpBytes;       // 转储文件的能存储的最大字节数。如果转储文件达到该大小限定,它将被关闭 。0值意味着无大小限制。
    UINT                MaxDumpPacks;       // 所能存储到转储文件中最大的数据包包数,如果转储文件达到该限定,它将被关闭 。0值意味着无此限制。
    BOOLEAN             DumpLimitReached;   // 如果转储文件的最大大小(MaxDumpBytesMaxDumpPacks)达到了,该值为TRUE
#ifdef HAVE_BUGGY_TME_SUPPORT
    MEM_TYPE            mem_ex;     // TME虚拟协处理器使用的内存  
TME_CORE            tme;        // 包含TME协处理器虚拟机的数据结构体
#endif //HAVE_BUGGY_TME_SUPPORT
    NDIS_SPIN_LOCK      MachineLock;// 保护BPF过滤器与TME引擎的自旋锁,如果在使用中
    UINT                MaxFrameSize;       // 底层MAC所能接收的最大帧大小。用来检测通过NPF_Write()NPF_BufferedWrite()发送的帧大小。
    // KAFFINITY 被用来作为一个位掩码,是为了系统的亲缘性。因此在每个被支持的操作系统上对所有系统上的CPU都是足够大的(x86上为32位,x64上为64?.
// 我们使用它的大小计算CPU的最大数目。
    CpuPrivateData      CpuData[sizeof(KAFFINITY) * 8];     // 内核缓冲区结构体的内存池,每个CPU一个
    ULONG               ReaderSN;           // 下一个将被从内核缓冲池中读取的数据包的序号
    ULONG               WriterSN;           // 下一个将被写入内核缓冲池中的数据包的序号。
// 这两个序号对每个捕获实例都是唯一的。
    ULONG               Size;           // 包含在CpuData字段中的每个内核缓冲区的大小
    ULONG              AdapterHandleUsageCounter;
    NDIS_SPIN_LOCK     AdapterHandleLock;
    ULONG              AdapterBindingStatus;    //// 描述NPF是否仍然被该实例使用的适配器所束缚,它是未绑定的或它是未束缚的。
    NDIS_EVENT         NdisOpenCloseCompleteEvent;
    NDIS_EVENT         NdisWriteCompleteEvent;  // 事件,当所有数据包被NdisSend成功发送后产生该事件通知(并且,对应的sendComplete函数被调用)
    NTSTATUS           OpenCloseStatus;
    ULONG              TransmitPendingPackets;  // 说明正被挂起的待传输数据包的数目,比如,已被提交给NdisSendXXX,但是SendComplete仍没被调用。
}OPEN_INSTANCE, *POPEN_INSTANCE;
 
其中Requests[MAX_REQUESTS]的类型为INTERNAL_REQUEST结构体,该结构体存储一个OID请求。驱动程序使用该结构体在底层NIC驱动程序上执行OID请求或者设置操作。通常仅网络驱动程序执行OID操作,但NPF通过一个IOCTL接口导出该机制给用户层应用程序。驱动程序使用该结构体来封装一个NDIS_REQUEST结构体。
结构体的具体定义如下:
typedef struct _INTERNAL_REQUEST {
    LIST_ENTRY  ListElement;        // 用来操控请求链表的句柄
    NDIS_EVENT  InternalRequestCompletedEvent;// 请求完成事件
    NDIS_REQUEST    Request;    // 带有实际请求的结构体,将由NdisRequest调用传递。
    NDIS_STATUS RequestStatus;
} INTERNAL_REQUEST, *PINTERNAL_REQUEST;
 
NPF 有四种工作模式,分别定义如下:
#define MODE_CAPT 0x0       // 捕获工作模式
#define MODE_STAT 0x1       // 统计工作模式
#define MODE_MON  0x2       // 内核监视模式
#define MODE_DUMP 0x10      // 内核转储工作模式

1.6.2    IRP的说明

NPF 驱动程序入口DriverEntry中的下列代码指定了IRP_MJ_CREATEIRP_MJ_CLOSEIRP_MJ_CLEANUPIRP_MJ_DEVICE_CONTROLIRP对应的处理函数。
DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open;
DriverObject->MajorFunction[IRP_MJ_CLOSE]  = NPF_Close;
DriverObject->MajorFunction[IRP_MJ_CLEANUP]= NPF_Cleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = NPF_IoControl;
 
函数PacketOpenAdapterNPFCreateFileA系统调用导致NPF_Open函数调用。
lpAdapter->hFile=CreateFileA(SymbolicLinkA,
GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_EXISTING,0,0);
 
函数 PacketCloseAdapter CloseHandle 系统调用导致NPF_CleanupNPF_Close函数调用。
CloseHandle(lpAdapter->hFile);
 
诸如PacketSetBuff函数中DeviceIoControl的系统调用导致NPF_IoControl函数调用。
Result = (BOOLEAN)DeviceIoControl(AdapterObject->hFile,
BIOCSETBUFFERSIZE,&dim,sizeof(dim),
NULL,0,&BytesReturned,NULL);
 

你可能感兴趣的:(职场,休闲,winpcap)