• WinPcap权威指南(一)


      WinPcap是一个开源的网络抓包模块,顾名思义,它只能工作在Windows下,但本文介绍的知识并不局限于任何操作系统和开发语言,因为网络协议本身是没有这些区别的。阅读本指南之前,请先下载WinPcap安装到自己的电脑上,目前WinPcap的最新版本是4.1.3,支持基于NT核心的所有操作系统(从NT4一直到Win8),读者可以从官方网站http://www.winpcap.org/install/default.htm下载。
        网络其实是分层设计的,一个应用层的网络程序对外通信的时候,大概的流程如下:应用程序(调用WinSock)<--->SPI层<--->TDI层<--->NDIS层<--->MiniPort层<-->物理网卡,实际上,应用程序可以直接调用上面的某一层,例如:直接调用TDI驱动收发网络包;又例如,一般软件防火墙工作于NDIS层,如果你直接使用MiniPort HOOK进行收发数据,那么防火墙是完全不知情的,也不会有任何提示。应用层抓包的话,方法有很多:例如本机的可以使用API HOOK,截取WinSock的调用;或者直接安装一个SPI的HOOK;又或者直接使用RawSocket接口。WinPcap的驱动实际上位于NDIS层,属于NDIS过滤驱动,也就是说,它只是抓包,但不像软件防火墙的passthru模型还可以拦截。另外,它还可以发送数据包。注意:笔者曾经将它翻译为Delphi,发现在以太网环境下(例如局域网)它是通过自己的驱动发布,而对于拨号上网的PPPOE连接,它调用的是微软的NetMon接口,也就是说,如果你的电脑没有安装NetMon,那么在拨号上网的环境下WinpCap可能是无法预期工作的。
        调用WinPcap有两种方式,一种是通过packet.dll调用它的驱动npf.sys,另外一种是通过wpcap.dll再间接调用packet.dll来调用它的驱动npf.sys,可能会有人问,既然可以直接调用packet.dll,为什么还要有调用wpcap.dll这种方式?原因很简单:在Unix下(或者Linux)下有一个抓包接口叫Pcap—这也是WinPcap的名称来由,wpcap.dll对外提供了相同的接口函数,便于程序员在不同的平台下移植。本文介绍的是packet.dll调用方式,对wpcap.dll接口感兴趣的朋友可以参考韩国程序员写的一个控件包Snoop2,我将它的下载放在本文的末尾(说句题外话,WinPcap的安装程序,实际上是释放sys和dll到系统,然后安装sys为服务,具体可以参考这个Snoop2(Jingtao修改版),里面直接集成了sys和dll,所以无需先安装WinPcap了,但是由于当时这个版本时间很早,所以还支持Win98系统,读者可以将里面的sys和dll文件替换成现在的最新版本再编译)。
        要调用WinPcap,第一步当然是打开需要操作的网卡,因为有可能一台电脑上有好几张网卡。WinPcap提供了一个函数叫PacketGetAdapterNames,用于获取系统已经安装的所有网卡,函数声明如下:
    
    TPacketGetAdapterNames = function(pStr: PAnsiChar; BufferSize: PULONG): Byte; cdecl;
     其中pStr是一个用于接收网卡名称的缓冲区,BufferSize是该缓冲区的大小,如果函数成功,则返回非0值。需要注意的是,网络的名称有多种形式,一种是设备名称,一种是友好名称,这个函数返回的格式是:设备名称+两个0+网卡名称,在WinPcap里面打开某块网卡,需要提供的是设备名称,packet.dll的源代码里面大概的实现如下:
    function PacketGetAdaptersNPF(): Byte;
    var
      LinkageKey, AdapKey, OneAdapKey: HKEY;
      RegKeySize: DWORD;
      Status: Longint;
      i: Integer;
      dim: DWORD;
      RegType: DWORD;
      TName: array[0..255] of AnsiChar;
      TAName: array[0..255] of AnsiChar;
      AdapName: array[0..255] of AnsiChar;
      TcpBindingsMultiString: PAnsiChar;
      FireWireFlag: UINT;
      //
      //    Old registry based WinPcap names
      //
      //    CHAR        npfCompleteDriverPrefix[MAX_WINPCAP_KEY_CHARS];
      //    UINT        RegQueryLen;
    
      npfCompleteDriverPrefix: array[0..MAX_WINPCAP_KEY_CHARS - 1] of AnsiChar; // = NPF_DRIVER_COMPLETE_DEVICE_PREFIX;
      DeviceGuidName: array[0..255] of AnsiChar;
    label tcpip_linkage;
    begin
      RegKeySize := 0;
      FillChar(npfCompleteDriverPrefix, sizeof(npfCompleteDriverPrefix), #0);
      StrCopy(npfCompleteDriverPrefix, NPF_DRIVER_COMPLETE_DEVICE_PREFIX);
      //TRACE_ENTER("PacketGetAdaptersNPF");
    
     //
     // Old registry based WinPcap names
     //
     // Get device prefixes from the registry
    
      Status := RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        'SYSTEMCurrentControlSetControlClass{4D36E972-E325-11CE-BFC1-08002BE10318}',
        0,
        KEY_READ,
        AdapKey);
    
      if (Status <> ERROR_SUCCESS) then
      begin
        //TRACE_PRINT("PacketGetAdaptersNPF: RegOpenKeyEx ( Class{networkclassguid} ) Failed");
        goto tcpip_linkage;
      end;
    
      i := 0;
    
      //TRACE_PRINT("PacketGetAdaptersNPF: RegOpenKeyEx ( Class{networkclassguid} ) was successful");
      //TRACE_PRINT("PacketGetAdaptersNPF: Cycling through the adapters in the registry:");
    
      //
      // Cycle through the entries inside the {4D36E972-E325-11CE-BFC1-08002BE10318} key
      // To get the names of the adapters
      //
      //while((Result = RegEnumKey(AdapKey, i, AdapName, sizeof(AdapName)/2)) == ERROR_SUCCESS)
      while ((RegEnumKey(AdapKey, i, AdapName, sizeof(AdapName) div 2)) = ERROR_SUCCESS) do
      begin
        Inc(i);
        FireWireFlag := 0;
        //
        // Get the adapter name from the registry key
        //
        Status := RegOpenKeyEx(AdapKey, AdapName, 0, KEY_READ, OneAdapKey);
        if (Status <> ERROR_SUCCESS) then
        begin
          //TRACE_PRINT1("%d) RegOpenKey( OneAdapKey ) Failed, skipping the adapter.",i);
          continue;
        end;
    
        //
        //
        // Check if this is a FireWire adapter, looking for "1394" in its ComponentId string.
        // We prevent listing FireWire adapters because winpcap can open them, but their interface
        // with the OS is broken and they can cause blue screens.
        //
        dim := sizeof(TName);
        Status := RegQueryValueEx(OneAdapKey,
          'ComponentId',
          nil,
          nil,
          PBYTE(@TName[0]),
          @dim);
    
        if (Status = ERROR_SUCCESS) then
        begin
          if (IsFireWire(TName) <> 0) then
          begin
            FireWireFlag := INFO_FLAG_DONT_EXPORT;
          end;
        end;
    
        Status := RegOpenKeyEx(OneAdapKey, 'Linkage', 0, KEY_READ, LinkageKey);
        if (Status <> ERROR_SUCCESS) then
        begin
          RegCloseKey(OneAdapKey);
          //TRACE_PRINT1("%d) RegOpenKeyEx ( LinkageKey ) Failed, skipping the adapter",i);
          continue;
        end;
    
        dim := sizeof(DeviceGuidName);
        Status := RegQueryValueExA(LinkageKey,
          'Export',
          nil,
          nil,
          PBYTE(@DeviceGuidName[0]),
          @dim);
    
        if (Status <> ERROR_SUCCESS) then
        begin
          RegCloseKey(OneAdapKey);
          RegCloseKey(LinkageKey);
          //TRACE_PRINT1("%d) Name = SKIPPED (error reading the key)", i);
          continue;
        end;
    
        if (strlen(DeviceGuidName) >= strlen('Device')) then
        begin
          // Put the DeviceNPF_ string at the beginning of the name
          StrPCopy(TAName, Format('%s%s', [npfCompleteDriverPrefix,
            DeviceGuidName + strlen('Device')]));
        end
        else
          continue;
    
        //terminate the string, just in case
        TAName[sizeof(TAName) - 1] := #0;
        //TRACE_PRINT2("%d) Successfully retrieved info for adapter %s, trying to add it to the global list...", i, TAName);
        // If the adapter is valid, add it to the list.
    
    
        PacketAddAdapterNPF(TAName, FireWireFlag);
    
        RegCloseKey(OneAdapKey);
        RegCloseKey(LinkageKey);
    
      end; // while enum reg keys
    
      RegCloseKey(AdapKey);
    
      tcpip_linkage:
      //
      // no adapters were found under {4D36E972-E325-11CE-BFC1-08002BE10318}. This means with great probability
      // that we are under Windows NT 4, so we try to look under the tcpip bindings.
      //
    
      //TRACE_PRINT("Adapters not found under SYSTEMCurrentControlSetControlClass. Using the TCP/IP bindings.");
    
      Status := RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        'SYSTEMCurrentControlSetServicesTcpipLinkage',
        0,
        KEY_READ,
        LinkageKey);
    
      if (Status = ERROR_SUCCESS) then
      begin
        // Retrieve the length of th binde key
        // This key contains the name of the devices as devicefoo
        //in ASCII, separated by a single ''. The list is terminated
        //by another ''
        Status := RegQueryValueExA(LinkageKey,
          'bind',
          nil,
          @RegType,
          nil,
          @RegKeySize);
    
        // Allocate the buffer
        TcpBindingsMultiString := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_ZEROINIT, RegKeySize + 2);
    
        if (TcpBindingsMultiString = nil) then
        begin
          //TRACE_PRINT("GlobalAlloc failed allocating memory for the registry key, returning.");
          //TRACE_EXIT("PacketGetAdaptersNPF");
          Result := 0;
          Exit;
        end;
    
        // Query the key again to get its content
        Status := RegQueryValueExA(LinkageKey,
          'bind',
          nil,
          @RegType,
          PBYTE(@TcpBindingsMultiString[0]),
          @RegKeySize);
    
        RegCloseKey(LinkageKey);
    
        // Scan the buffer with the device names
        i := 0;
        while True do
        begin
          if (TcpBindingsMultiString[i] = #0) then
            break;
    
          StrPCopy(TAName, Format('%s%s', [npfCompleteDriverPrefix, TcpBindingsMultiString + i + strlen('Device')]));
          //
          // TODO GV: this cast to avoid a compilation warning is
          //            actually stupid. We shouls check not to go over the buffer boundary!
          //
          Inc(i, strlen(PAnsiChar(TcpBindingsMultiString + i)) + 1);
    
          // If the adapter is valid, add it to the list.
          PacketAddAdapterNPF(TAName, 0);
        end;
    
        GlobalFreePtr(TcpBindingsMultiString);
      end
    
      else
      begin
    
      end;
      Result := 1;
    end;
        另外,IpHlp函数也提供了获取网卡信息的接口,而且网卡名称跟WinPcap的一样。由于IpHlp可以获取网卡的更多信息,例如:IP地址、物理地址、网关IP等信息,所以我们可以结合它来实现更加友好的选择界面。下面是本讲实现的最终效果图:

    这里写图片描述

  • 相关阅读:
    Java生产环境线上栈故障排查问题(COPY)
    Java集合HashMap,List底层
    图算法--染色法判定二图
    图算法--kruskal
    图算法--最小生成树prim
    图算法--判负环
    图算法--floyd
    图算法--spfa
    图算法--bellman-ford (nm)
    图算法--堆优化版dijkstra
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/10488092.html
Copyright © 2020-2023  润新知