基于SPI的数据报过滤原理与实现
一.个人防火墙技术概述
二.Winsock 2 SPI介绍
三.相关程序代码分析
四.小结与后记
五.附录之源代码
一)个人防火墙技术概述
随着网络安全问题日益严重,广大用户对网络安全产品也越来越关注。防火墙作为一种网络安全工具,早已受到大家的青睐。在PC机上使用的个人防火墙,很大程度上成为广大网民的安全保护者。Windows下的个人防火墙都是基于对数据报的拦截技术之上。当然在具体的实现方式上它们却有很大的不同。总的来说可分为用户级和内核级数据报拦截两类。其中内核级主要是TDI过滤驱动程序,NDIS中间层过滤驱动程序,NDIS过滤钩子驱动程序等,它们都是利用网络驱动来实现的;而用户级的过滤包括SPI接口,Windows2000包过滤接口等。本文主要讲述基于SPI的包过滤实现,它是Winsock 2的一个新特性。
二)Winsock2 SPI介绍
Winsock 2 是一个接口,而不是协议,所以它可以用于发现和使用任意数量的底层传输协议所提供的通信能力。起初的Winsock是围绕着TCP/IP协议运行的,但是在Winsock 2中却增加了对更多传输协议的支持。Winsock 2不仅提供了一个供应用程序访问网络服务的Windows socket应用程序编程接口(API),还包含了由传输服务提供者和名字解析服务提供者实现的Winsock服务提供者接口(SPI)和ws2_32.dll。本文仅讨论传输服务提供者及其应用,暂不对名字解析服务提供者进行分析。
Winsock 2的传输服务提供者是以动态链接库的形式(DLL)存在的,它是通过WSPStartup函数为上层函数提供接口,而其他的传输服务提供者函数则是通过分配表的方式来访问WS2_32.DLL。传输服务提供者的动态链接库只有在应用程序需要时才由Ws2_32.dll来装入内存中的,在不需要时则会被自动卸载。以下是winsock 2在传输服务提供者上的WOSA(Windows开放服务结构):
----------------------------
|Windows socket 2 应用程序|
----------------------------Windows socket 2 API
| WS2_32.DLL |
----------------------------Windows socket 2 传输SPI
| 传输服务提供者(DLL) |
----------------------------
Windows socket SPI在服务提供者中使用了以下的函数前缀命名方式:WSP(Windows socket服务提供者),WPU(Windows socket提供者向上调用),WSC(Windows socket配置)。每一个传输服务提供者都有它自己所支持的传输协议,它是使用WSAPROTCOL_INFOW结构来实现的。传输服务提供者把所有的相关信息都存放在这个结构中,而应用程序就是通过这个结构的内容来将自己和相应的传输服务提供者相关联。
Windows socket SPI提供三种协议:分层协议,基础协议和协议链。分层协议是在基础协议的上层,依靠底层基础协议实现更高级的通信服务。基础协议是能够独立,安全地和远程端点实现数据通信的协议,它是相对与分层协议而言的。协议链是将一系列的基础协议和分层协议按特点的顺序连接在一起的链状结构,请参见下图:
API------------------------
| WS2_32.DLL |
SPI------------------------
| 分层协议 |
SPI-------------
| 分层协议 |
SPI------------------------
| 基础协议 |
------------------------
Ws2_32.dll数据传输部分的主要功能是在服务提供者和应用程序之间提供流量管理的功能。每个应用程序通过Ws2_32.dll和相应的服务提供者进行严格的数据交换。Ws2_32.dll根据应用程序在创建套接字时所提供的参数来选择特定的服务提供者,然后把应用程序的实现过程转发由所选创建套接字的服务提供者来管理。也就是说,Ws2_32.dll只是一个中间过程,而应用程序只是一个接口,数据通信的实现却是有服务提供者来完成的。我们说过,Ws2_32.dll是通过创建套接字的API函数WSASocket或socket的参数来确定使用哪一个服务提供者。而WSASocket/socket的参数中包括了地址族,套接字类型和协议类型,这三个因素共同决定了创建套接字的服务提供者。Ws2_32.dll在服务提供者中寻找第一个和前面三因素相匹配的WSAPROTOCOL_INFOW结构,然后就调用这个WSAPROTOCOL_INFOW结构相应的WSPStartup函数,(所有的数据传输服务提供者以DLL的形式,它们对外的接口就只有WSPStartup,其他的服务提供者函数都是通过WSPStartup来调用的),进而调用如WSPSocket的函数来创建套接字,WSPConnect的函数来建立连接等等。除了流量管理功能外,Ws2_32.dll还提供了其他的服务,比如协议枚举,基于线程的阻塞钩子管理和在Ws2_32.dll和服务提供者之间进行版本协商。
传输服务提供者实现的功能包括建立连接,传输数据,实现流控制和差错控制等函数。其实Ws2_32.dll并不知道服务提供者的请求等活动是如何实现了,Ws2_32.dll在应用程序和服务提供者之间实现了媒介的功能。传输服务提供者可分为两类:套接字描述符是可安装的文件系统(IFS)句柄的提供者;剩下的是非IFS的提供者。在我们的程序中选用了非IFS提供者。可见,服务提供者实现了底层的与网络相关的协议。Ws2_32.dll提供了介质级别的流量管理,应用程序则提供了有关如何实现网络相关的操作,它实现了用户所希望的功能。
在传输服务提供者的实现过程中,安装顺序是非常重要的。我们不仅要正确的安装服务提供者,而且还必须在Windows socket中注册,将相关的系统信息保存在数据库中,这样Ws2_32.dll才能够方便的获得下层服务提供者的相关信息。在Ws2_32.dll中提供了用来安装服务提供者的函数WSCInstallProvider,它需要服务提供者的有关数据,比如DLL的名称和路径。同时Ws2_32.dll还提供了卸载服务提供者的函数WSCDeinstallProvider,在不需要时通过它将特定的服务提供者从系统中删除。为什么说传输服务者的安装顺序很重要呢?在服务提供者配置函数中的WSCEnumProtocols是用来枚举系统中所有已安装的服务提供者,它按照服务提供者的安装顺序相应的列出他们。在前面我们也提到过,Ws2_32.dll在服务提供者中按安装顺序搜寻和WSASocket/socket提供的三个参数相匹配的服务提供者,所以安装顺序在一定程度上是决定了服务提供者是否被正确调用的关键。Windows socket 2还提供了一个动态链接库Sporder.dll,它提供了对已安装的所有服务提供者顺序的重新排列(此DLL系统没有自带,common目录中已提供)。在附录中的T-Sporder.exe是一个查询当前已安装所有数据传输服务提供者属性的工具。
服务提供者系统中区分基础协议,分层协议和协议链是通过结构WSAPROTOCOL_INFOW中的Protocolchain结构的ChainLen值来实现的。分层协议的ChainLen值为0,基础协议的值为1,而协议链的值是大于1。在数据传输服务提供者的实现方式中分层协议和基础协议几乎是相同的,它们的不同之处在安装上。Windows中,现有的系统服务提供者(系统自带)几乎已提供了所有基本的服务,因此我们所写的服务提供者程序,都可以对数据报进行适当“修饰”后调用系统服务提供者来完成绝大部分剩下的功能,无论是基础服务提供者还是分层服务提供者都可以使用这种技术,免去不必要的劳动。基础服务提供者的实现过程主要是替换当前系统服务提供者的安装路径为自己的服务提供者的安装路径即可,当然我们必须保存所以系统服务者的相关数据,在我们卸载自己的服务提供者还原系统服务提供者时要用到这些信息,如系统服务者DLL的名称和路径。而协议链就不同了,首先我们必须安装好所有的基础协议和分层协议后,再构造协议链的WSAPROTOCOL_INFOW结构链,组成协议链的每个协议都会在协议链的ProtocolChain.ChainEntries数组中被定义,协议链数组中的第一个协议应该是第一个分层服务提供者。当然在安装分层协议及协议链时我们不会改变系统服务提供者,最多只是改变系统服务提供者的安装顺序罢了。在此,我们以分层服务提供者为例来说明数据传输服务提供者的安装过程。
Ws2_32.dll是使用标准的动态链接库来加载服务提供者接口的DLL到系统中去的,并调用WSPStartup来初始化。WSPStartup是Windows Socket 2应用程序调用SPI程序的初始化函数,也就是入口函数。WSPStartup的参数LPWSAPROTOCOL_INFOW指针提供应用程序所期望的协议信息,然后通过这个结构指针我们可以获得所保存的系统服务提供者的DLL名称和路径,加载系统服务提供者后查找到系统SPI程序的WSPStartup函数的指针,通过这个指针我们就可以将自己服务提供者的WSPStartup函数和系统SPI程序的WSPStartup函数相关联,进而调用系统的各个服务提供者函数。在数据传输服务提供者的实现中,我们需要两个程序,一个是可执行文件用来安装传输服务提供者;另一个就是DLL形式的数据传输服务提供者。下面我们就对安装程序(instif.exe)和实现程序(ipfilter.dll)所使用的主要函数进行简要分析。
在上一篇博客《Activity双击事件监听》中,使用手势识别类GestureDetector可以用标准的SDK类实现双击监听,但存在一个局限性:由于GD是在OnTouchEvent方法中拦截事件进行处理,而Android事件派发流程,使得如果Activity内部的View会消耗点击事件,GD就无法接受到点击事件了。
为此,本人实现一个可以完美监听Activity事件的工具类:DoubleClick。
类的全部源码:
public class DoubleClick{ private int clickCount; private long firstClickTime; private int CLICK_DELAY = 300; private final static int MOVE_OFFSET = 20; private float mLastMotionY; private float mLastMotionX; private Timer cleanClickTimer = new Timer(); private OnDoubleClickListener listener; public DoubleClick(OnDoubleClickListener l){ listener = l; } public interface OnDoubleClickListener{ void onDoubleClick(); } public void dispatchTouchEvent(MotionEvent event){ final float y = event.getY(); final float x = event.getX(); if (event.getAction() == MotionEvent.ACTION_DOWN) { mLastMotionY = y; mLastMotionX = x; clickCount++; if(clickCount == 1){ firstClickTime = System.currentTimeMillis(); //超过监听时间50MS还没有再次点击,则将点击次数,点击事件清零。 cleanClickTimer.schedule(new TimerTask() { @Override public void run() { clickCount = 0; firstClickTime = 0; } }, CLICK_DELAY+50); }else if(clickCount == 2){ long secondClickTime = System.currentTimeMillis(); if (secondClickTime - firstClickTime <= CLICK_DELAY) { listener.onDoubleClick(); } clickCount = 0; firstClickTime = 0; } } if(event.getAction() == MotionEvent.ACTION_MOVE){ final int yDiff = (int) Math.abs(y - mLastMotionY); final int xDiff = (int) Math.abs(x - mLastMotionX); boolean yMoved = yDiff > MOVE_OFFSET; boolean xMoved = xDiff > MOVE_OFFSET; // 判断是否是移动 if (yMoved || xMoved) { clickCount = 0; firstClickTime = 0; } } } }
使用实例:
private DoubleClick doubleClick; //当需要使用双击事件监听,调用此方法即可。 protected void enableDClickReturn(){ doubleClick = new DoubleClick(new DoubleClick.OnDoubleClickListener() { @Override public void onDoubleClick() { //这里我的项目需要的功能是双击返回上一级Activity finish(); } }); } @Override public boolean dispatchTouchEvent(MotionEvent event) { if(doubleClick == null) { return super.dispatchTouchEvent(event); }else{ doubleClick.dispatchTouchEvent(event); } return super.dispatchTouchEvent(event); }
更多Android项目工具类,请关注本人GitHub主页。
除公司项目源码不能公开,本人所有源码都开源到Github上:http://github.com/chenyoca
前面讲解过BB10 Cascades环境中应用签名的安装和调试令牌的使用,不过忘了讲应用的签名了,有人发邮件过来问,应用开发好以后如果希望发布,该如何给应用签名,于是补上现在这篇文章,介绍BB10 Cascades上应用签名的过程。
在BB10 Cascades环境中,如果希望对开发测试好的应用进行签名,需要打开“bar-descriptor.xml”文件来进行,当然前提是你的BB10 Cascades环境已经安装了BlackBerry 10的签名。
打开项目,双击打开“bar-descriptor.xml”文件,可以看到类似下面的界面,选择右上方的“export release build”按钮,如下图中红框框中的部分:
然后系统会弹出一个对话框,如下图,里面填写的是项目的名称,导出的目录等,这里不需要对设置进行修改,只需要记住项目导出的路径即可,不然导出了都不知道去哪里找。不过希望自己指定导出的目录,可以点击“Export to folder”一栏右边的“Borwse...”按钮选择自己喜欢的目录,最后点击“Finish”按钮。
接着系统又弹出一个对话框,如下图,注意这里的“Enable digital signing”一定是要在选中状态,否则不会给应用签名,点击Finish按钮就可以对应用进行签名了。
点击了"Finish"按钮后系统会提示输入签名的密码,这个是你在安装签名的时候设置的密码,有不清楚的请回去看我安装签名的那篇文章:
http://blog.csdn.net/keyboardota/article/details/8084131
还有一个需要注意的是签名时开发机器必须是连接了网络的,因为签名时需要访问BlackBerry的站点。
签名后就可以再你指定的目录中找到签名后的应用文件了,文件名格式是你的项目名加上版本号,缺省导出目录是:
<workspace目录>\<项目目录>\arm\o.le-v7
好,可以向BlackBerry App World提交你的应用了!
1楼berryreload前天 16:54mark