现在的位置: 首页 > 综合 > 正文

WINDOWS网络包过滤技术

2013年10月13日 ⁄ 综合 ⁄ 共 9049字 ⁄ 字号 评论关闭
待整理...

 一、user-mode网络包过滤  
  1、winsock分层service   provider  
  参照Microsoft   Platform   SDK上有关文档和例子  
  (http://www.microsoft.com/msdownload/platformsdk/sdkupdate/)  
  这里有好几个microsoft   lsp   例子,最新(可能最bug-free)的经常在这里能找到。需要知道的是可以通过TDI调用核心TCPIP驱动,而且可以完全绕开WINSOCK,在大多 数情况下这不是一个问题。例如:QOS的实现可以在WINSOCK LSP上。  
 然而,这样做的话,程序必须察看和操作每个包,而不能依靠WINSOCK LSP,他们要以一种接近核心态的方法来实现。  
  2、win2000包过滤接口  
    WIN2000包过滤接口提供了一种机制,这种机制允许用户态程序或者服务指定一系列的"过滤原则",这些过滤原则会被低层的TCPIP实现用来过滤包。这种过滤工主要是对IP原地址、目标地址、端口号(或者端口号范围)进行pass或者drop操作。  
  Windows   Developer's   Journal  
  《用iphlpapi.dll进行包过滤》作者:Ton   plooy,October,2000,Volume   11,   Number   10。  
    WIN2000提供了一个较好对TCPIP的可编程控制,其中包括包过滤。不幸的是,有关这个新的API的文档并不是很容易能找到。这篇文章向你演示了怎样对特定IP地址或者特定TCP端口的包进行阻塞的编程。  
  链接:www.wdj.com  
  上面这个例子的下载:ftp://ftp.wdj.com/pub/webzip/1110/plooy.zip  
  Hollis   的解决方案:  
    HTS W2K IpHook例子演示了IP过滤和它的HOOK API,包含原文件,而且是免费的,需要HtsCpp运行时库(免费),下载地址:http://www.hollistech.com/  
  3、winsock替代DLL  
    在使用WINSOCK LSP之前,唯一的办法是用自己的DLL取代微软的WINSOCK DLL,假如实现顺利的话,自己的DLL会接收用户的WINSOCK调用请求,然后还可以调用原来的WINSOCK DLL来处理。  
    不过这样的实现是比较费力的,其中有个困难就是微软的WINSOCK DLL里面经常有一些未公开的内部使用的函数,一个WINSOCK代替DLL至少要处理其中的一些未公开函数。  
    随着WINDOWS系统结构的变化,有些方面得到了加强,比如系统文件保护,这使得这种技术变得不太可行。总的说来,使用WINSOCK DLL替换不是一个坏主意。(Xfilter就是用的这种技术,原代码可能在网上有流传,我以前看到过的)  
  二、kernel-mode网络包过滤  
  1、Transport   Data   Interface   (TDI)  
 这主要是一个直接在核心TCPIP驱动上面的一层过滤驱动。在WINXP上TDI驱动是一种传统的NT风格的驱动,使用了基于IRP的API,这里有两种方法来实现。  
  A、使用核心模式服务的IoAttachDeviceXYZ函数族在TDI上实现一个过滤。  
  B、对TDI驱动IRP DISPATCH表进行过滤。  
    IoAttachDeviceXYZ函数在许多WINNT驱动开发的书上提到。这两种技术都需要对WINNT驱动开发编程技术十分了解,对TDI函数也要相当的了解。  
  2、NDIS中间层(IM)  
  具体请看NDIS   IM   FAQ:http://www.pcausa.com/resources/ndisimfaq.htm  
  3、WIN2000 FILTER-HOOK    
 请参照有关DDK文档,系统中只能有一个活动的Filter-Hook存在,这点使这种技术的使用有严重的限制。(平时所见的drvipflt就是用的这个)  
  4、WIN2000 FIREWALL-HOOK     
    Firewall-Hook   Driver函数在文档里介绍得很少,而且在有些win2000版本中不可用。请参照微软有关文档:http: //msdn.microsoft.com/library/default.asp?url=/library/en-us/network/hh/network/firewall_3wfb.asp  
  5、NDIS-HOOKING    (费尔防火墙就是用的这种技术吧,据我所知,虽然我没看过原码。)  
  NDIS-Hooking驱动拦截或者叫"HOOK"一些由NDIS封装程序导出的函数。虽然从实现手段上来说有些不正规,但一个有系统的NDIS-Hooking过滤会非常有效。  
  另外:NDIS-Hooking过滤驱动有下面的好处:  
  A、容易安装(可以动态装卸,不过有时候会出问题,里面有些情况现在还未知。)  
  B、支持拨号-ppp适配器。  
    Ndis-Hooking技术在98和ME系统下非常有效和实用。在这些平台上,DDK文档和provided   services都能很有用的帮你HOOK由Ndis   wrapper导出的函数。  
 Ndis-Hooking技术在NT,2000和XP上同样有效和实用。这种技术很像核心模式的调试器。文档支持较少,而且基本上不会被WHQL认证。  
  PCAUSA提供了一套NDIS PIM驱动例子,这些例子能在现有的WIN平台上运行成功(从95到XP)。地址:http://www.pcausa.com/ndispim/Default.htm  
   
  其他:  
  Network操作和进程信息:  
    有许多人想知道网络上的操作和WIN进程(就是应用程序啦)之间怎样联系起来,举例来说,可能会想知道是哪个进程在一个特定的IP端口上发送或接收数据。  
 先不考虑这种技术是否有用,或者是否可靠,我们认 为核心模式TCPIP驱动上层的过滤程序可以处理这个问题。而TCPIP驱动下层的过滤程序根本看不到进程信息。特别要注意的是有些网络服务操作生成一个 新的进程attach到系统进程上的。在这种情况下进程信息并不能告诉我们原先是哪个进程生成的。特别是单独在核心模式下的WIN服务(TDI客户)  
    最后,有必要看看下面的资料United   States   Patent   5,987,611;   "System   and   methodology   for   managing   internet   access   on   a   per   application   basis   for   client   computers   connected   to   the   internet   "  
    我们并不知道这项专利的价值,也不知道他是否能用在包过滤上。详情请参阅:http://www.uspto.gov/patft/index.html  
  www.pcausa.com  
   
  ============================================  
  drvipflt具体解析,就是上面所提到的吧(2-3就是说的这东东)。  
  假定大家对驱动框架已经有了一定的理解。IRP分配程序如下:  
  NTSTATUS   DrvDispatch(IN   PDEVICE_OBJECT   DeviceObject,   IN   PIRP   Irp)  
  {  
  ...  
        switch   (irpStack->MajorFunction)  
          {  
  ...  
          case   IRP_MJ_DEVICE_CONTROL:  
                  ioControlCode   =   irpStack->Parameters.DeviceIoControl.IoControlCode;  
   
                  switch   (ioControlCode)  
                  {  
  //   ioctl   code   to   start   filtering  
  //这里可以从用户模式程序发送这样的请求。  
  //直接用DeviceIoControl这个函数,就像下面这样调用就可以了吧,我想。  
  //DeviceIoControl(drivehandle,START_IP_HOOK,NULL,0,NULL,0,&bytereturned,NULL)  
  case   START_IP_HOOK:  
  {  
  //这个应该是最主要的函数了。  
                        SetFilterFunction(cbFilterFunction);  
   
  break;  
  }  
   
  //   ioctl   to   stop   filtering  
  case   STOP_IP_HOOK:  
  {  
  SetFilterFunction(NULL);  
                           
  break;  
  }  
   
                          //   ioctl   to   add   a   filter   rule  
  case   ADD_FILTER:  
  {  
  if(inputBufferLength   ==   sizeof(IPFilter))  
  {  
  IPFilter   *nf;  
   
  nf   =   (IPFilter   *)ioBuffer;  
   
  AddFilterToList(nf);  
  }  
   
  break;  
  }  
   
  //   ioctl   to   free   filter   rule   list  
  case   CLEAR_FILTER:  
  {  
  ClearFilterList();  
   
  break;  
  }  
   
  default:  
  Irp->IoStatus.Status   =   STATUS_INVALID_PARAMETER;  
   
   
  break;  
                  }  
   
                  break;  
  ...  
  }  
  SetFilterFunction(cbFilterFunction)可能是最重要的一个程序了。具体如下:  
  实际上这个做法相当在系统中注册了一个回调函数。  
  NTSTATUS   SetFilterFunction(PacketFilterExtensionPtr   filterFunction)  
  {  
  NTSTATUS   status   =   STATUS_SUCCESS,   waitStatus=STATUS_SUCCESS;  
  UNICODE_STRING   filterName;  
  PDEVICE_OBJECT   ipDeviceObject=NULL;  
  PFILE_OBJECT   ipFileObject=NULL;  
   
  PF_SET_EXTENSION_HOOK_INFO   filterData;  
   
  KEVENT   event;  
  IO_STATUS_BLOCK   ioStatus;  
  PIRP   irp;  
   
 //首先获得一个设备指针。  
  //first   of   all,   we   have   to   get   a   pointer   to   IpFilterDriver   Device  
  RtlInitUnicodeString(&filterName,   DD_IPFLTRDRVR_DEVICE_NAME);  
  status   =   IoGetDeviceObjectPointer(&filterName,STANDARD_RIGHTS_ALL,   &ipFileObject,   &ipDeviceObject);  
  if(NT_SUCCESS(status))  
  {  
  //一些初始化工作,填充filterData。  
  //initialize   the   struct   with   functions   parameters  
  filterData.ExtensionPointer   =   filterFunction;  
   
  //we   need   initialize   the   event   used   later   by   the   IpFilterDriver   to   signal   us  
  //when   it   finished   its   work  
  KeInitializeEvent(&event,   NotificationEvent,   FALSE); 

//这个就是最重要的注册回调函数过程。DDK中具体讲述是这样的  
  //IOCTL_PF_SET_EXTENSION_POINTER   registers   filter-hook   callback   functions   to   the   IP   filter   driver    
  //to   inform   the   IP   filter   driver   to   call   those   filter   hook   callbacks   for   every   IP   packet    
  //that   is   received   or   transmitted.   Also,   IOCTL_PF_SET_EXTENSION_POINTER   clears   filter-hook    
  //callback   functions   from   the   IP   filter   driver.   (看到了吧,最后一句话,注册新的回调函数,就将原先的清除掉了,  
  //所以说系统中只存在一个这样的驱动有用。)  
  //we   build   the   irp   needed   to   establish   fitler   function这个地方仅仅是生成这样的IRP,并没有注册  
  irp   =   IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,  
              ipDeviceObject,  
  (PVOID)   &filterData,  
  sizeof(PF_SET_EXTENSION_HOOK_INFO),  
  NULL,  
  0,  
  FALSE,  
  &event,  
  &ioStatus);  
   
   
  if(irp   !=   NULL)  
  {  
  //   we   send   the   IRP  
  //这个地方才是真正的注册呀。  
  status   =   IoCallDriver(ipDeviceObject,   irp);  
   
  //and   finally,   we   wait   for   "acknowledge"   of   IpDriverFilter  
  if   (status   ==   STATUS_PENDING)    
  {  
  waitStatus   =   KeWaitForSingleObject(&event,   Executive,   KernelMode,   FALSE,   NULL);  
   
  if   (waitStatus   !=   STATUS_SUCCESS   )   {}  
  }  
   
  status   =   ioStatus.Status;  
   
  if(!NT_SUCCESS(status)){}  
  }  
   
  else  
  {  
  //if   we   cant   allocate   the   space,   we   return   the   corresponding   code   error  
  status   =   STATUS_INSUFFICIENT_RESOURCES;  
   
  }  
   
  if(ipFileObject   !=   NULL)  
  ObDereferenceObject(ipFileObject);  
   
  ipFileObject   =   NULL;  
  ipDeviceObject   =   NULL;  
  }  
   
  else  
   
  return   status;  
  }  
  //真正的过滤函数是这个,在最早的IRPdispatch里面传递的这个函数。  
  //这个函数就是系统传递了一个包头和包内容和包长度之类的东西,你可以在里面进行一些处理,  
  //假如你想让这个包通过的话,就返回PF_FORWARD,或者你不想让包通过的话,就返回PF_DROP就拦住了。是不是  
  //听起来很简单,  
  PF_FORWARD_ACTION   cbFilterFunction(IN   unsigned   char   *PacketHeader,IN   unsigned   char   *Packet,   IN   unsigned   int   PacketLength,   IN   unsigned   int   RecvInterfaceIndex,   IN   unsigned   int   SendInterfaceIndex,   IN   unsigned   long   RecvLinkNextHop,   IN   unsigned   long   SendLinkNextHop)  
  {  
  IPPacket   *ipp;  
  TCPHeader   *tcph;  
  UDPHeader   *udph;  
   
  int   countRule=0;  
   
  struct   filterList   *aux   =   first;  
   
  //we   "extract"   the   ip   Header    
  ipp=(IPPacket   *)PacketHeader;  
   
  // dprintf("Source:   %x/nDestination:   %x/nProtocol:   %d",   ipp->ipSource,   ipp->ipDestination,   ipp->ipProtocol);  
   
  //TCP   ->   protocol   =   6  
  //we   accept   all   packets   of   established   connections  
  if(ipp->ipProtocol   ==   6)  
  {  
  tcph=(TCPHeader   *)Packet;    
   
  // dprintf("FLAGS:   %x/n",   tcph->flags);  
   
  //if   we   havent   the   bit   SYN   activate,   we   pass   the   packets  
  if(!(tcph->flags   &   0x02))    
  return   PF_FORWARD;  
  }  
   
  //otherwise,   we   compare   the   packet   with   our   rules  
  while(aux   !=   NULL)  
  {  
  // dprintf("Comparing   with   Rule   %d",   countRule);  
   
  //if   protocol   is   the   same....  
  if(aux->ipf.protocol   ==   0   ||   ipp->ipProtocol   ==   aux->ipf.protocol)  
  {  
  //we   look   in   source   Address  
  if(aux->ipf.sourceIp   !=   0   &&   (ipp->ipSource   &   aux->ipf.sourceMask)   !=   aux->ipf.sourceIp)  
  {  
  aux=aux->next;  
   
  countRule++;  
  continue;  
  }  
   
  //   we   look   in   destination   address  
  if(aux->ipf.destinationIp   !=   0   &&   (ipp->ipDestination   &   aux->ipf.destinationMask)   !=   aux->ipf.destinationIp)  
  {  
  aux=aux->next;  
   
  countRule++;  
  continue;  
  }  
   
  //if   we   have   a   tcp   packet,   we   look   in   ports  
  //tcp,   protocol   =   6  
  if(ipp->ipProtocol   ==   6)    
  {  
  if(aux->ipf.sourcePort   ==   0   ||   tcph->sourcePort   ==   aux->ipf.sourcePort)  
  {    
  if(aux->ipf.destinationPort   ==   0   ||   tcph->destinationPort   ==   aux->ipf.destinationPort)   //puerto   tcp   destino  
  {  
  //now   we   decided   what   to   do   with   the   packet  
  if(aux->ipf.drop)  
    return     PF_DROP;  
  else  
  return   PF_FORWARD;  
  }  
  }  
  }  
   
  //udp,   protocol   =   17  
  else   if(ipp->ipProtocol   ==   17)    
  {  
  udph=(UDPHeader   *)Packet;    
   
  if(aux->ipf.sourcePort   ==   0   ||   udph->sourcePort   ==   aux->ipf.sourcePort)    
  {    
  if(aux->ipf.destinationPort   ==   0   ||   udph->destinationPort   ==   aux->ipf.destinationPort)    
  {  
  //now   we   decided   what   to   do   with   the   packet  
  if(aux->ipf.drop)  
  return     PF_DROP;  
   
  else  
  return   PF_FORWARD;  
  }  
  }  
  }  
   
  else  
  {  
  //for   other   packet   we   dont   look   more   and   ....  
  //now   we   decided   what   to   do   with   the   packet  
  if(aux->ipf.drop)  
  return     PF_DROP;  
  else  
  return   PF_FORWARD;  
  }  
  }  
   
  //compare   with   the   next   rule  
  countRule++;  
  aux=aux->next;  
  }  
   
  //we   accept   all   not   registered  
  return   PF_FORWARD;  
  }

抱歉!评论已关闭.