使用NDIS驱动监测以太网络活动

本论文提供了NDIS的基本的理解,应用程序如何与驱动程序交互,发挥驱动程序最佳性能。本论文也说明了使用例子驱动(PACKET.SYS)监测以太网的应用程序。本论文不是帮助程序员开发网络驱动而是帮助他使用这样的驱动。
       从计算机被发明以来,对大多数编程人员来说,编写设备驱动都是令人着迷的。开发驱动是为了满足特定应用的需要。这个目的导致大量不同种类的驱动被开发出来,例如打印机驱动,文件系统驱动等。此外,特定的应用与特定种类的驱动相对应。随着Internet的到来,编写网络驱动成为驱动开发的核心。为帮助开发网络设备驱动,微软为Windows NT操作系统开发了网络设备接口规范(NDIS)库。
1.NDIS简介
1.1什么是设备驱动
设备驱动是操作系统和输入输出设备间的粘合剂。驱动负责将操作系统的请求传输,转化为特定物理设备控制器能够理解的命令。
1.2什么是网络接口卡
       网络接口卡(NIC)是一个物理设备,类似于网关,通过它,网络中的任何设备都可以发送和接收数据帧。不同网络中,网络接口卡的名称是不同的。例如,以太网中称为以太网接口卡,令牌环网中称为令牌环网接口卡,等等。
1.3什么是网络驱动接口规范(NDIS)
       网络驱动接口规范描述了一个接口,通过这个接口,一个或多个NIC驱动可以与一个或多个覆盖在其上的协议驱动和操作系统通信。NDIS为网络驱动开发提供了完整的抽象。对所有外部功能来说,NIC驱动都依赖于NDIS,这些功能包括与协议驱动的通信,注册,截获NIC硬件中断,与下层的NIC的通信。
1.4为什么需要NDIS
       NDIS库(NDIS.SYS)为NIC驱动的编写提供了完整的抽象接口。库输出所有可以在NIC驱动开发中使用的NT内核模式函数。这个库负责响应所有下层NIC驱动特定的任务,保持绑定和状态信息。
2.NDIS特点与使用
2.1 NDIS基本特征
l         单NIC驱动
l         NDIS库
l         均衡处理器支持
l         多协议驱动支持
l         管理
l         发送类型
l         操作标志
l         全双工操作
l         ARCNET和WAN支持
2.2 NDIS驱动类型
       Windows NT支持三种类型的驱动:
l         网络接口卡驱动(NIC)
l         中间协议驱动
l         上层协议驱动
下图说明了Windows NT网络驱动的构成
2.2.1网络接口卡驱动
NIC驱动管理网络接口卡(NIC)。NIC驱动接口在下边界直接控制硬件(NIC),在上边界提供上层驱动访问的接口:
l         发送和接收包
l         重置NIC(Reset)
l         停止NIC
l         查询NIC
l         设置NIC操作特性
NIC驱动的两种类型
l         微端口驱动:微端口驱动应用于管理NIC硬件特殊操作,包括在NIC上发送和接收数据。微端口驱动不能直接呼叫系统例程,只能呼叫NDIS提供的函数。
l         完全NIC驱动:完全NIC驱动不仅管理硬件而且管理NDIS完成的的操作系统特定任务。完全NIC驱动必须保持接收数据的绑定信息。
      2.2.2中间协议驱动
       中间协议驱动接口位于上层协议驱动和微端口驱动之间。对于上层传输驱动程序来书,中间驱动看起来像是微端口驱动。对微端口驱动来说,看起来像是协议驱动。使用中间协议驱动的主要是为了传输媒质,存在于对于传输驱动未知和微端口管理之间的新的媒质类型。
      2.2.3上层协议驱动
       上层协议驱动应用于TDI接口或其它向用户提供服务的特定应用接口。例如驱动调用NDIS分配包,向包中烤贝数据和向底层发送包。它也在它的底层提供协议接口,来接收下层驱动发送来的包。
2.3应用程序和驱动的交互
       在Windows NT下的所有驱动必须具有DriverEntry函数,作为驱动的进入点。驱动中其它的函数是通过DriverEntry函数声明的。应用程序调用函数如CreateFile,ReadFile等,会由NT I/O管理器生成相应IRP(输入/输出请求包)。在NT下,几乎所有I/O操作都是包驱动的。每个I/O操作由工作顺序来描述,工作顺序告诉驱动做什么和通过I/O子系统追踪请求的过程。这些工作顺序通过一个称为I/O请求包(IRP)的数据结构的形式给出。这个IRP为完成特定操作按顺序调用驱动中的进入点。
3.在以太网中使用NDIS监测数据包 
              本节以一个NDIS驱动例子说明监测以太网。
              Packet监测捕获在局域网中传输的的所有包。这是由以太网的广播特性决定的。通过以太网发送的包是向网络中所有计算机广播的。每台计算机上的以太网卡检查每个包的目的是否是它自己,如果是则接收,不是则拒绝。利用这个基本的功能来捕获网络中传输的所有数据包。以太网卡可以被设置工作于好几种模式,可以设置于期望中的捕获数据包的模式。一个这样的可以工作于期望的例子驱动程序PACKET.SYS来自微软Windows NT 设备开发工具(DDK)。
3.1以太网接口卡的模式
       以太网接口卡可以被设置的模式如下:
l         广播
l         多播
l         直接
l         混杂
       广播:数据帧可以发向网络中所有计算机,这种帧的目的地址是0xffffff,也称为帧的广播地址。任何设置为广播模式的网卡都接收目的地址为广播地址的数据帧。通常所有的网卡被配置为接收广播帧。
       多播:发往一组计算机的帧称为多播帧,使用特定的多播地址作为目的地址。这些计算机的组构成了多播组。这样,多播组里的任何一个成员计算机将会接收具有多播目的地址的帧。尽管网卡可以不是一个多播组里成员,但可以配置为多播模式,这样就可以接收所有多播帧。
       直接:发往特定计算机的帧具有特定计算机的物理地址(以太网地址)的目的地址。具有特定物理地址的计算机将接收特定的帧,丢弃其它的帧。网卡可以设置为仅仅接收直接帧。
       混杂:设置为这个模式的网卡接收所有收到的数据包。这个模式具有的以太网的广播特性是包监测应用程序的关键。
       PACKET.SYS例子驱动可以将网卡设置为以上提到的模式。应用可以利用PACKET.SYS将网卡设置为混杂模式,以便于捕获网络中传输的所有数据包。
3.2关于例子驱动PACKET.SYS
       例子PACKET.SYS来自NT DDK。这个驱动可以将网卡设置为任何期望的模式,并且允许应用程序通过网络发送和接收数据包。除驱动程序的sys文件之外,还提供了一个DLL(PACKET32.DLL),通过此DLL应用程序可以和驱动程序通信。
      3.2.1应用程序如何同PACKET.SYS通信
上面的图表清楚地描述了应用程序和PACKET.SYS驱动的交互。应用程序调用DLL中的函数,这些函数依次调用PACKET.SYS驱动中的进入点。驱动程序利用NDIS.SYS输出函数与网络接口卡通信。
3.2.2如何在应用程序中使用PACKET32.DLL
应用程序使用驱动读取所有到达NIC的数据包。以下示例说明了这个过程。
在上述过程中使用的结构体定义如下:
typedef struct _ADAPTER
        {
        //保存由CreateFile方法返回的句柄
        HANDLE     hFile;
        //保存驱动的符号链接名
        TCHAR      SymbolicLink[MAX_LINK_NAME_LENGTH];
        } ADAPTER, *LPADAPTER;
 
        typedef struct _PACKET
        {
        // Holds the handle to the event that is associated with the adapter object. 保存和适配器对象相应的事件句柄
        HANDLE       hEvent;
// The OVERLAPPED structure contains information used in   
// asynchronous input and output 包含异步输入输出信息的OVERLAPPED结构
        OVERLAPPED   OverLapped;
        // Buffer that holds the data sent or received.包含发送和接收数据的缓冲区
        PVOID        Buffer;
        // Length of the Buffer.缓冲区长度
        UINT         Length;
        } PACKET, *LPPACKET;
 
typedef struct _CONTROL_BLOCK
{
    // Pointer to the Adapter object.指向适配器对象的指针
    LPADAPTER   hFile;
    // handle to the event.保存事件句柄
    HANDLE      hEvent;   
    // Name of the driver as registered in the registry.注册表中注册的驱动名
    TCHAR       AdapterName[64];        
   // Buffer where the received packet data is stored. 保存接收数据包的缓冲区         
    HANDLE      hMem;
    LPBYTE      lpMem;
    // Buffer where the packet data to be sent is stored. 保存发送数据包的缓冲区
    HGLOBAL     hMem2;
    LPBYTE      lpMem2;       
  // Specifies the Packet length.包长度
  ULONG        PacketLength;
 // Specifies the Last read packet size.     最后读取的包大小   
 ULONG        LastReadSize;
 // Specifies the Buffer length.缓冲区长度
UINT         BufferSize;
 } CONTROL_BLOCK, *PCONTROL_BLOCK;
 
The implementation code begins here. 应用程序开始
// Variables used in the patch code.插入代码的变量
CONTROL_BLOCK cbAdapter;
// Get the Adapter name from the registry.从注册表中得到适配器名字
1. ULONG NameLength=64;
2. PacketGetAdapterNames(
3. CbAdapter.AdapterName,
4. &NameLength
5. );
6. //reserve 1514 bytes, maximum frame size.保留1514字节,最大帧长度
7. CbAdapter.BufferSize=1514;
 
8. // Allocate and lock the memory buffers to send and receive packets.分配并锁定内存缓冲区用于发送和接收数据包
9. CbAdapter.hMem=GlobalAlloc(GPTR, 1514);
 
10. CbAdapter.lpMem=(LPBYTE)GlobalLock(CbAdapter.hMem);
 
11. CbAdapter.hMem2=GlobalAlloc(GPTR,1514);
 
12. CbAdapter.lpMem2=(LPBYTE)GlobalLock(CbAdapter.hMem2);
 
13. //Open the underlying adapter for retrieving packets.打开优先的适配器用于接收数据
14.CbAdapter.hFile=(ADAPTER*)PacketOpenAdapter(CbAdapter.AdapterName);
 
15. // OpenAdapter failed.打开适配器域
16. if (CbAdapter.hFile = = NULL)
17. {
18.          AfxMessageBox("Open Adapter failed");
19. }
 
20. // A void pointer that receives the packet pointer returned by PacketAllocatePacket.由PacketAllocatePacket返回的接收数据包空指针
21. PVOID Packet;
 
22. //Set Filter for the non-selective mode.设置过滤条件为非选择模式
23. Filter=NDIS_PACKET_TYPE_PROMISCUOUS;
 
24. //set the card in Promiscous mode.设置网卡为混杂模式
25. PacketSetFilter(
26. CbAdapter.hFile,
27. Filter
28. );
 
29. // Allocate buffer to receive the packet data.分配缓冲区用于接收数据包
30. Packet=PacketAllocatePacket(CbAdapter.hFile);
 
31. // Initialize the packet receive buffer.初始化接收数据包缓冲区
32. if(Packet != NULL)
33. {
34.         PacketInitPacket(
35.         (PACKET *)Packet,
36.         (char *)pdData[nCurrentWriteLocation].pData,
37.         1514
38. );
 
39. // Read the packet from the Underlying driver. 从驱动中读取数据包
40. PacketReceivePacket(
41. CbAdapter.hFile,
42. (PACKET *)Packet,
43. TRUE,
44. &pdData[nCurrentWriteLocation].nPacketLength
45. );
The above patch of code clearly describes how the application uses the PACKET.SYS driver to place the Ethernet Interface Card in promiscuous mode to capture all the packets that travel through the network.
上述插入代码描述了应用程序如何使用PACKET.SYS驱动设置以太网接口卡为混杂模式,用来捕获所有网络上的数据包。
代码分析
(1-5)
       这几行调用DLL中的函数PacketGetAdapterNames,得到驱动在注册表中注册的名字。
(1-13)
       这几行初始化所有的适配器对象的变量和缓冲区,锁定已经分配的全局缓冲区。
(14-19)
这几行代码调用DLL中的函数PacketOpenAdapter,函数依次调用CreateFile方法,调用驱动程序中相应的进入点。为随后的读写操作打开适配器。
(23-28)
这几行代码调用DLL中的PacketSetFilter函数,设置以太网接口卡为期望的模式。这里,过滤模式设置为混杂(非选择)模式。此函数依次呼叫DeviceIoControl方法,用来设置网卡工作于期望的模式。
(31-38)
这几行代码调用DLL中的PacketInitPacket函数初始化Packet驱动对象。此Packet对象用于从网络上接收所有数据的包。
(40-45)
调用DLL中的PacketReceivePacket函数,此函数依次调用ReadFile方法来读取通过EIC从网络上收到的数据。
以上代码清晰的描述了应用程序利用例子驱动PACKET.SYS将EIC设置为期望的模式,发送和接收数据包。
 
3.2.3应用程序中使用的在Packet32.dll中定义的函数如下:
ULONG
PacketGetAdapterNames(
    PTSTR   pStr,
    PULONG BufferSize
   )
{
    HKEY       SystemKey;
    HKEY       ControlSetKey;
    HKEY       ServicesKey;
    HKEY       NdisPerfKey;
    HKEY       LinkageKey;
    LONG       Status;
    DWORD      RegType;
    // Open the Key HKEY_LOCAL_MACHINE,打开HKEY_LOCAL_MACHINE键值
    Status=RegOpenKeyEx(
               HKEY_LOCAL_MACHINE,
               TEXT("SYSTEM"),
               0,
               KEY_READ,
               &SystemKey
               );
    if (Status == ERROR_SUCCESS) {
       // Open the key currentcontrolset 打开currentcontrolset键值
        Status=RegOpenKeyEx(
                   SystemKey,
                   TEXT("CurrentControlSet"),
                   0,
                   KEY_READ,
                   &ControlSetKey
                   );
        if (Status == ERROR_SUCCESS) {
            // Open the key Services打开Services键值
            Status=RegOpenKeyEx(
                       ControlSetKey,
                       TEXT("Services"),
                       0,
                       KEY_READ,
                       &ServicesKey
                       );
            if (Status == ERROR_SUCCESS) {
        // Open the key Packet. 打开Packet键值
        Status=RegOpenKeyEx(
                           ServicesKey,
                           TEXT("Packet"),
                           0,
                           KEY_READ,
                           &NdisPerfKey
                           );
                if (Status == ERROR_SUCCESS) {
            // Open the key Linkage.打开Linkage键值
                    Status=RegOpenKeyEx(
                               NdisPerfKey,
                               TEXT("Linkage"),
                               0,
                               KEY_READ,
                               &LinkageKey
                               );
                    if (Status == ERROR_SUCCESS) {
                   // Open the key Export.     
                        Status=RegQueryValueEx(
                                   LinkageKey,
                                   TEXT("Export"),
                                   NULL,
                                   &RegType,
                                   (LPBYTE)pStr,
                                   BufferSize
                                   );
             // Close all the keys that have been opened so far.关闭已打开的所有键值
                        RegCloseKey(LinkageKey);
                    }
                    RegCloseKey(NdisPerfKey);
                }
                RegCloseKey(ServicesKey);
            }
            RegCloseKey(ControlSetKey);
        }
        RegCloseKey(SystemKey);
    }
    return Status;
}
上面的函数PacketGetAdapterNames返回注册表中注册的驱动名。
 
1. PVOID
2. PacketOpenAdapter(
3. LPTSTR    AdapterName
4. )
5. {
6. LPADAPTER lpAdapter;
7. BOOLEAN     Result;
 
8. ODS("Packet32: PacketOpenAdapter/n");
 
// allocates global memory for the adapter object.为适配器对象分配全局内存
9. lpAdapter=(LPADAPTER)GlobalAllocPtr(
10. GMEM_MOVEABLE | GMEM_ZEROINIT,
11. sizeof(ADAPTER)
12. );
 
13. if (lpAdapter==NULL) {
 
14. ODS("Packet32: PacketOpenAdapter GlobalAlloc Failed/n");
15. return NULL;
16. }
 
// Copy the name into the symbolic link member. 将名字拷贝到符号链接名
17. wsprintf(
18. lpAdapter->SymbolicLink,
19. TEXT(".//%s%s"),
20. DOSNAMEPREFIX,
21. &AdapterName[8]
22. );
 
// Defines an MS-DOS name for the device.
23. Result=DefineDosDevice(
24. DDD_RAW_TARGET_PATH,
25. &lpAdapter->SymbolicLink[4],
 
26. AdapterName
27. );
 
28. if (Result)
29. {
30. // Creates and returns a file handle for the specified device. 为特定设备创建并返回文件句柄
31. lpAdapter->hFile=CreateFile(lpAdapter->SymbolicLink,
32. GENERIC_WRITE | GENERIC_READ,
33. 0,
34. NULL,
35. CREATE_ALWAYS,
36. FILE_FLAG_OVERLAPPED,
37. 0
38. );
 
39. if (lpAdapter->hFile != INVALID_HANDLE_VALUE) {
40.         return lpAdapter;
41.         }
42. }
43. ODS("Packet32: PacketOpenAdapter Could not open adapter /n");
 
44. GlobalFreePtr(
45. lpAdapter
46. );
47. return NULL;
48. }            
上述图表是应用程序调用的一个函数的流图。此图也适用于和驱动交互的其他函数。函数PacketOpenAdapter为设备定义了一个新的DOS设备名,调用CreaetFile方法来创建并打开通信设备,得到指向设备的句柄。应用程序预先调用此函数来发送和接收数据包。CreateFile方法调用驱动中指定为IRP_MJ_CREATE的进入点,此进入点调用NDIS库,输出NdisOpenAdapter函数打开适配器。
 
 
PVOID
PacketAllocatePacket(
    LPADAPTER   AdapterObject
    )
{
LPPACKET     lpPacket;
    // Allocates memory for the Packet object. 为Packet对象分配内存
    lpPacket=(LPPACKET)GlobalAllocPtr(
                             GMEM_MOVEABLE | GMEM_ZEROINIT,
                             sizeof(PACKET)
                             );
    if (lpPacket==NULL) {
        ODS("Packet32: PacketAllocateSendPacket: GlobalAlloc Failed/n");
        return NULL;
    }
    // Create an event that will be signaled when the operation is over.操作结束时建立事件对象
    lpPacket->OverLapped.hEvent=CreateEvent(
                        NULL,
                        FALSE,
                        FALSE,
                        NULL
                        );
    if (lpPacket->OverLapped.hEvent==NULL) {
        ODS("Packet32: PacketAllocateSendPacket: CreateEvent Failed/n");
        GlobalFreePtr(lpPacket);
        return NULL;
    }
    return lpPacket;
}
以上的函数PacketAllocatePacket为packet对象分配内存,调用CreateEvent函数来建立特定文件句柄的事件。
 
VOID
PacketInitPacket(
    LPPACKET    lpPacket,
    PVOID       Buffer,
    UINT        Length
    )
{
    // Set packet object's buffer to the buffer.设置packet对象缓冲区到缓冲区
    lpPacket->Buffer=Buffer;
   // Set packet object's buffer Length to the buffer Length.设置packet对象缓冲区长度到缓冲区长度
    lpPacket->Length=Length;
}
此函数设置packet对象缓冲区。
 
BOOLEAN
PacketReceivePacket(
    LPADAPTER   AdapterObject,
    LPPACKET    lpPacket,
    BOOLEAN     Sync,
    PULONG      BytesReceived
    )
{
    BOOLEAN      Result; 
 // Set offset value to 0.设置偏移量为0
    lpPacket->OverLapped.Offset=0;
    lpPacket->OverLapped.OffsetHigh=0;
    if (!ResetEvent(lpPacket->OverLapped.hEvent)) {
        return FALSE;
    }
    // Call ReadFile to read a packet. 调用ReadFile来读取数据包
    Result=ReadFile(
              AdapterObject->hFile,
              lpPacket->Buffer,
              lpPacket->Length,
              BytesReceived,
              &lpPacket->OverLapped
              );
    if (Sync) {
        // They want to wait
        Result=GetOverlappedResult(
                   AdapterObject->hFile,
                   &lpPacket->OverLapped,
                   BytesReceived,
                   TRUE
                   );
    }
    else
   {
         // They don't want to wait, they will call PacketWaitPacket to get
        // The real result
        //不等待,调用PacketWaitPacket得到真实值
        Result = TRUE;
    }
    return Result;
}
 
此函数调用驱动中适当的进入点来读取网络中获得的数据包,放入声明的缓冲区。ReadFile方法使用在CreateFile方法中获得的句柄来完成此操作。
使用驱动PACKET.SYS监测有许多例程是有用的。注意,没有提供更多的应用细节。本节仅仅说明了使用NDIS例子驱动PACKET.SYS监控所有网络动作的应用程序如何编写。
 
4.如何识别HTTP请求
       上节描述了怎样从以太网中捕获所有的数据包。我们的意图是不仅仅捕获所有数据包,并且监控网络上的Internet活动,这意味着我们应该识别携带HTTP请求的数据包。这需要以太网帧结构和IP,TCP/UDP包在帧中的封装的只是。本节描述了TCP/IP包的标识,HTTP请求的标识,帧中的URL信息的获取。
4.1 TCP/IP网络数据流
       为了在分层的网络中传输数据,从应用程序传输数据到协议栈中中相应的协议。之后,此协议处理完数据之后,将数据传向栈中的下一个协议。在数据穿越每一层协议的同时,协议栈上相应协议为了栈中下一层协议,将数据封装起来。因此,封装就是一个将数据存储成协议栈中更低层协议要求的格式的过程。
因此,我们可以看出,应用程序模块封装从用户应用消息传来的数据。TCP模块封装应用数据,增加TCP头并且发往下一层。当数据传向网络栈中IP模块时,IP模块将TCP段格式化为IP报文或称为包。以太网驱动将IP模块传来的数据格式化并将数据放入以太网帧中。这就解释了帧中如何封装IP报文,IP包如何封装TCP/UDP数据。为了识别HTTP请求,首先,我们应该识别包为TCP/IP包,然后检查此包是否是TCP包,最后识别此包是否HTTP请求。
4.2识别TCP/IP包
       为识别一个数据包是TCP/IP包,我们应该首先看看以太网的帧结构。
以太网的帧数据包含了14字节头。
       以太网帧头的域是:
l         目的地址(6字节)
l         源地址(6字节)
l         帧类型(2字节)
       就像名字的含义,目的地址域说明了以太网帧的目的。类似的,源地址说明了帧的源。帧的类型域是我们关心的。这个域标识了帧的协议。
       如果包是有效的IP包,则帧类型域(第13和第14字节)将会是080016。
4.3识别TCP包
       识别了TCP/IP包之后,下一个任务是识别出包是否是TCP包或其他包。因为HTTP请求仅仅通过TCP请求来传输,所以我们可以忽略其他包。为确定包是否是一个TCP包,我们必须分析IP头。IP头如下所示:
IP头的重要域是:
l         头部长度(4位)和版本号(4位)
l         包总长度(2字节)
l         数据在传输层上使用的协议类型。下表说明了IP包的通用TCP/IP协议类型域
Protocol
Value(Decimal)
TCP
6
UDP
17
ICMP
1
IGMP
2
l         头部校验和(2字节)
l         源IP(4字节)
l         目的IP(4字节)
       IP头中协议域的值为06说明了数据包是TCP包。
4.4识别HTTP请求
              识别出数据包是TCP包后,我们必须识别包是否是HTTP请求包。为找出包中是否包含HTTP请求,我们必须检查TCP头,TCP头如下所示:
       TCP头中重要的域是:
l         源端口(2字节)
l         目的端口(2字节)
l         序列号(4字节)
l         标识号(4字节)
l         Hlen,保留和代码位(2字节)
l         窗口(2字节)
l         校验和(2字节)
l         紧急点(2字节)
       如上所述,TCP头中,我们最关心的域是源和目的端口域。这些域说明了连接建立的端口。不同的TCP服务比如HTTP,FTP等等使用特定的端口号来提供他们的服务。
       对HTTP服务来说,端口号是008010或005016。如果源端口域包含值是80(HTTP端口号),则包就是HTTP响应包。如果目的端口域是80,则包是HTTP请求包。
4.5获取URL
       现在我们已经发现包是一个HTTP请求包了,找到包中包含的URL地址就相对简单了。
       在浏览器中键入的请求被以GET或POST请求的形式被发HTTP服务器。浏览器添加其他的与浏览器相关的信息,发往相应的HTTP服务器。完整的信息包含了封装在数据包中的请求页的URL。因此,依靠分析数据包就可以得到URL。URL出项在数据包中的通用格式为:
       GET/HTTP/1.0
       以上的URL是我们键入浏览器的任何站点默认页。如果我们请求站点的其他页或点击页面中提供的链接,然后请求页也被放入GET请求中。例如,当我们请求的页面为sample.html,则URL将会是:
GET/sample.html HTTP/1.0
       因此,通过在数据包中查找GET串,就可以获取URL请求。
 
5. 驱动性能和操作系统兼容性
       在混杂模式下,驱动捕获到达网卡的所有数据包。如果网络流量很大,就有可能不能捕获而丢包。这必须考虑在内。驱动中使用的是NDIS版本是3.0。驱动在Windows NT下工作良好,但不能在Windows95下工作,因为Windows95仅仅支持的是NDIS是2.1版本和NDIS 3.1版本。
WINDOWS CE 中使用驱动的相关问题
       Windows CE中NDIS应用是Windows NT下NDIS4.0的一个子集。为Windows NT写的驱动代码可以在Windows CE下工作,但需要考虑到一些问题。才Windows CE下,驱动会被编译为DLL,而不是Windows NT下的.SYS文件。而且,CE不支持内建的DMA和分配连续的内存块。更进一步,为CE编写NDIS驱动,程序员必须考虑到电源管理的问题。这就是说,必须提供附加的电源管理代码。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值