基于IMD的包过滤防火墙原理与实现
一、前言
二、IMD中间层技术介绍
三、passthru例程分析
四、部分演示代码
五、驱动编译与安装
六. 总结
一、前言
前段时间,在安全焦点上看到了TOo2y朋友写的《基于SPI的数据报过滤原理与实现》,很是不错。文章中提到的基于SPI的数据报拦截技术是在用户级的。用户级的拦截有其优势,实现方便、便于移植、通用性强,但是,用户级并不能得到所有的数据报。本文提到的基于IMD的数据报过滤则是属于内核级的,它建立在网络驱动上面。
其实两个月前就想写这篇文章了,可惜这个技术是我一年前做的,好久不用,已经忘的差不多了,所以一直没有写。不过,近来正巧有个课题,牵扯到了中间层驱动。于是,重新拾起丢了有一年的DDK,啃了起来。这期间,真的要非常感谢linxder的帮忙,让我少走了一些弯路,否则,我真的要从头学起了。废话不多说了,切入正题。
二、中间层驱动技术介绍
中间层驱动,英文为NDIS intermediate driver。
1)内核级网络驱动介绍
Microsoft Windows 2000支持三种基本的内核级网络驱动,这三层driver顺序从下到上依次为:
1. Miniport NIC drivers:微端口网卡驱动,位于最底层,直接操纵网卡并且对高层驱动提供接口。
2. Intermediate drivers:IMD中间层驱动,这就是今天的主角,位于1和3之间,具体的作用下面就会介绍。
3. Protocol drivers:高层协议驱动,俗称为TDI(传输驱动程序接口),高于前面两层,直接面向用户级,
为用户提供网络服务,也就是绝大多数程序所用到的网络接口。
2)IMD驱动
IMD中间层,它的实质很简单,最经典的描述莫过于下面的话:
An intermediate driver is typically layered over one or more NDIS NIC drivers and under a transport driver (possibly multilayered) that supports TDI at its upper edge.
An NDIS intermediate driver exports MiniportXxx functions at its upper edge and ProtocolXxx functions at its lower edge.(见DDK文档)
中间层插入网卡和协议层之间,对上面的协议层表现为一个虚拟的微端口网卡结构,而对下面的网卡则表现为一个协议层的结构。所以,无论是网卡接收并上传的数据报,还是上层要下送至网卡发送的数据报,无一例外地要经过中间层。
3)IMD包过滤技术
前面我们已经看到,所有的数据报都要经过中间层,所以,我们可以在中间层加入我们想要过滤的数据报的特征,实现基于中间层驱动的内核级包过滤。
这样做的优势非常明显,首先,在驱动级别上做过滤,无须组包,速度快,效率自然就高;其次,所有的数据报无一例外,只要网卡上传的数据报均可以截获,避免了用户级无法得到所有数据报的缺点。当然,世界上没有完美的事情。IMD包过滤技术也存在其不可避免的缺点,与操作系统版本关系密切,与硬件联系大,可移植性低。我在调试这个驱动的时候,就碰到了无数次蓝屏,无数次重启动,进了几次安全模式,甚至还为此重新安装了一次系统。
正是由于上面的一些问题,现在市面上还没有见到有厂家推出基于IMD的实用型防火墙,大部分都是在实验室中的作品,或许真的是要做到通用性很难吧,不过还是希望能尽快见到这样的产品面世。
三、passthru代码分析
到这里,你或许已经非常想看看到底怎么来实现基于IMD的包过滤防火墙了,不过,你肯定会迟疑,如果让我们自己写整个中间层驱动的话,是不是有些太艰难了啊?况且,我只是个搞网络安全的,我不是专门写驱动的,让我完成一个驱动程序,还要对上层协议伪装成一个网卡,对下层伪装成一个协议层,这不是要命么?
呵呵,其实微软很不错的,在提出这项技术之后,其DDK中附带了一个中间层驱动的例程,就是passthru。passthru实现了一个中间层的基本功能,对下表现为一个协议层的驱动,对上表现为一个虚拟网卡,安装passthru驱动之后,你可以在硬件管理中的网卡中看到一个虚拟网卡。不过,passthru只是插入到网卡和上层协议中间,却未做任何工作,也就是说,passthru只是让所有的数据报原原本本地流经自己而已。我们要想实现中间层包过滤的功能,需要对passthru进行修改。
想想我们要实现的包过滤的功能,我们只需要在中间层接收到数据报的时候进行规则判断就可以了,而在passthru中,接收数据报是用protocol.c文件中的PtReceive和PtReceivePacket这两个函数来实现的。根据微软的解释,微软建议接收包用PtReceivePacket函数,因为可以得到更高的效率,然而,为了向下兼容,也保留了PtReceive函数给老的网卡使用。所以,在一块网卡上,只可能有一个函数在工作,这要取决于你的网卡型号了。巧的是,我的两台机器的网卡应用的函数正巧不一样。同样是IBM的机器,一台P4 1.5G的机器的网卡是Realtek RTL8139(A) PCI Fast Ethernet Adapter,另外一台P4 2.0G的机器的网卡是Intel(R) PRO/100 VE Network Connection,其中Realtek网卡用的是PtReceive来接包,而Intel的网卡是用PtReceivePacket来接包。
现在我们知道了哪个函数负责接收数据报,那么我们就可以对这个函数进行修改了。从兼容和通用性考虑,我们需要对PtReceive和PtReceivePacket函数进行修改,其中加上我们需要判断的规则进行过滤,下面就贴详细的代码了。
四、部分演示代码
我们的目的是在调用接受数据报函数的时候能执行我们的过滤代码,所以,我们要在函数代码中添加我们自己的代码,下面用过滤特定协议类型的数据报来做演示。
首先修改PtReceive,看一下protocol.c文件中函数的代码,代码中用NdisGetReceivedPacket函数得到一个PNDIS_PACKET的结构Packet,数据报内容就存放在这个结构中的链表内。我们定义一个PUCHAR结构的pPacketContent,然后用下面的代码获得整个数据报的内容:
//---------------------------------------------------------
int PacketSize;
PUCHAR pPacketContent;
PUCHAR pBuf;
UINT BufLength;
MDL * pNext;
UINT i; //把数据包内容从Packet拷贝到pPacketContent NdisQueryPacket( Packet,NULL,NULL,NULL,&PacketSize); Status= NdisAllocateMemory( &pPacketContent, , ,HighestAcceptableMax);
if (Status!=NDIS_STATUS_SUCCESS ) return Status;
NdisZeroMemory (pPacketContent, ); NdisQueryBufferSafe(Packet->Private.Head, &pBuf, &BufLength, );
NdisMoveMemory(pPacketContent, pBuf, BufLength); i = BufLength;
pNext = Packet->Private.Head; for(;;)
{
if(pNext == Packet->Private.Tail)
break;
pNext = pNext->Next; //指针后移
if(pNext == NULL)
break; NdisQueryBufferSafe(pNext,&pBuf,&BufLength,);
NdisMoveMemory(pPacketContent+i,pBuf,BufLength);
i+=BufLength;
} //数据拷贝完毕
//---------------------------------------------------------
现在,我们已经在PtReceive函数中得到了数据报的内容,存放在pPacketContent中,数据报的格式你可以去查书。通常,在以太网中,得到的数据报大致是如下结构,以太帧头14个字节,放在pPacketContent[0]到pPacketContent[13]中,其中前六个字节是目的MAC地址,然后六个字节源MAC地址,然后两个字节是协议类型,通常的协议类型有0x08 0x00 ->IP,0x08 0x06 ->ARP,0x08 0x35 ->RARP,所以,可以通过pPacketContent[12]和pPacketContent[13]来判断协议类型。如果是IP包,然后pPacketContent中存放的是IP头,根据IP头的格式,可以得到第23个字节pPacketContent[23]表示传输层协议:1 ->ICMP,2 ->IGMP,6 ->TCP,17 ->UDP,剩下的就是数据报内容了。因为我们只是做演示,所以只要知道这几个标志性的就好了,其他的你可以根据你的需要扩展。我们通过pPacketContent中的内容可以做些规则,比如过滤ICMP包,我们只要比较pPacketContent[12]和pPacketContent[13]还有pPacketContent[23]这三个标志位就可以了,如果不是ICMP包,那么不做任何工作,如果匹配了,那就返回一个NDIS_STATUS_NOT_ACCEPTED,将包丢弃,释放pPacketContent,就可以过滤ICMP包了,下面就是过滤规则的代码。
//---------------------------------------------------------
//规则标志位(1表示过滤,0表示放行,你可以通过改这个数值来配置规则)
UINT ICMP = ; //ICMP数据报规则
UINT IGMP = ; //IGMP数据报规则
UINT TCP = ; //TCP数据报规则
UINT UDP = ; //UDP数据报规则 //规则判断
if (ICMP == )
{
if(((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == )
{
DbgPrint("ICMP被拦截!\n");
NdisFreeMemory(pPacketContent, , );
return NDIS_STATUS_NOT_ACCEPTED;
}
} if (IGMP == )
{
if(((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == )
{
DbgPrint("IGMP被拦截!\n");
NdisFreeMemory(pPacketContent, , );
return NDIS_STATUS_NOT_ACCEPTED;
}
} if (TCP == )
{
if(((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == )
{
DbgPrint("TCP被拦截!\n");
NdisFreeMemory(pPacketContent, , );
return NDIS_STATUS_NOT_ACCEPTED;
}
} if (UDP == )
{
if(((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == &&
((char *)pPacketContent)[] == )
{
DbgPrint("UDP被拦截!\n");
NdisFreeMemory(pPacketContent, , );
return NDIS_STATUS_NOT_ACCEPTED;
}
} //规则判断结束
//---------------------------------------------------------
到这里,PtReceive函数已经修改完了,只要调用PtReceive函数接收数据报的时候,就可以执行我们的规则了,下面修改PtReceivePacket函数,其实上面的修改代码内容都是一样的,只不过PtReceivePacket函数跟PtReceive函数不太一样,PtReceivePacket直接在入口参数中就得到了PNDIS_PACKET的结构Packet,存放了数据报的所有内容,所以,直接将上面的代码粘到函数PtReceivePacket的代码中就可以了。到现在,无论是网卡调用哪一个接收函数,都可以执行我们需要的规则。
其实,上面所定义的规则是很简单的,因为只作为演示用,而我的课题嘛,嘿嘿,就不是那么简单了。其实在pPacketContent已经得到了所有的数据报内容,我们可以随意扩展规则。比如,过滤指定IP、指定端口的数据报,至于具体应该设置哪个位置,你只要去找一本介绍数据报结构的书看一下就知道了,呵呵。
五、驱动编译与安装
前面谈了那么多,其实都是在说IMD包过滤的实现和驱动代码,但是,我已经修改了代码,怎么编译和安装呢?
这一部分就解决驱动编译和安装的问题。我们用passthru的代码,对接收数据报的部分做了修改。然后,我们用DDK自带的builder工具来编译。我一般是用开始菜单中DDK程序组中的Free Build Environment,到passthru目录下执行build -cz来编译passthru,得到passthru.sys文件,然后再到NTDDK\src\network\config\filter目录下,执行build -cz来得到sfilter.dll,然后再加上passthru目录下的netsf.inf和netsf_m.inf,一共四个文件。这样,驱动安装所需要的文件就全了。
打开网络属性,添加服务,找到passthru目录下,安装,弹出没有数字签名的警告,不理,继续安装,最后,你会发现,网络属性中增加了一个名为Sample Filter的组件,同时,硬件管理器中的网卡下增加了一个Sample Filter Miniport的设备。如果你已经到了这里,并且系统没有出现蓝屏和死机,那么恭喜你,你已经成功地安装上了中间层驱动,并且已经发挥包过滤作用了。
六. 总结
难得我写了那么多,现在做一个总结吧。上文中的代码,我只是用来做演示的,只用来说明基于IMD的包过滤防火墙原理与实现,其实需要做的工作还有很多,驱动上面碰到的问题会很多,并且常常伴随着的是蓝屏,死机,甚至要重装系统。
你也许会问,那我每次改一下规则难道还要重新再安装一遍驱动么,况且,必须重启动一次,旧的驱动才彻底卸载掉,多麻烦啊。呵呵,其实是中间层驱动是可以跟应用层的程序结合的。首先你要在驱动中指定好规则,应用层的程序通过DeviceIoControl传递指令到驱动中,可以控制驱动的规则标志位。
特别推荐驱动开发网(www.driverdevelop.com)论坛的ndis网络接口开发版,这里是国内水平最高的地方,所有的与驱动有关的问题你都可以在那里得到解决。
最后,附带说一句,调试驱动的时候先做好处理系统后事的准备,对于出现的任何系统问题也好,硬盘问题也好,我不负任何责任,呵呵。
欢迎访问我们团队的站点:711网络安全小组 http://www.cpyy.net
基于IMD的包过滤防火墙原理与实现的更多相关文章
- Linux内核--基于Netfilter的内核级包过滤防火墙实现
测试内核版本:Linux Kernel 2.6.35----Linux Kernel 3.2.1 原创作品,转载请标明http://blog.csdn.net/yming0221/article/de ...
- 基于linux内核包过滤技术的应用网关
目录 基于linux内核包过滤技术的应用网关 硬件形态 基本原理 应用场景 主要功能 其他功能 客户定制 基于linux内核包过滤技术的应用网关 硬件形态 基本原理 应用场景 媒体内容过滤和深度识别 ...
- iptable防火墙原理
iptable防火墙原理 简介 Linux 2.0 ipfs/firewalld Linux 2.2 ipchain/firewall Linux 2.4 iptables/netfilter (ip ...
- 基于SPI的数据报过滤原理与实现
一.个人防火墙技术概述 随着网络安全问题日益严重,广大用户对网络安全产品也越来越关注.防火墙作为一种网络安全工具,早已受到大家的青睐.在PC机上使用的个人防火墙,很大程度上成为广大网民的安全保护者.W ...
- Linux网卡驱动安装、防火墙原理
安装网卡驱动程序: 需要检查是否安装kernel依赖包: rpm –q kernel-devel #检查kernel依赖包是否安装 yum –y install kernel-devel 检查gcc和 ...
- PfSense基于BSD的软件防火墙的安装、配置与应用
PfSense基于BSD的软件防火墙的安装.配置与应用 PfSense是一个FreeBSD下的免费开源的防火墙和路由器软件,他为了在X86平台上面建立一个高集成性的防火墙项目,下面就为大家展示如何配置 ...
- 写了个Linux包过滤防火墙
花几天写了个so easy的Linux包过滤防火墙,估计实际意义不是很大.防火墙包括用户态执行程序和内核模块,内核模块完全可以用iptable代替.由于在编写的过程一开始写的是内核模块所以就直接用上来 ...
- 1、架构--架构图、Iptables(简介、四表五链、流程图、使用、扩展模块)、包过滤防火墙
笔记 1.画架构图 2.Iptables 1.1 什么是防火墙 防止别人恶意访问. 1.2 防火墙种类 硬件防火墙 F5 软件防火墙 iptables firewalld 安全组 3.Iptables ...
- 基于JDK的动态代理原理分析
基于JDK的动态代理原理分析 这篇文章解决三个问题: What 动态代理是什么 How 动态代理怎么用 Why 动态代理的原理 动态代理是什么? 动态代理是代理模式的一种具体实现,是指在程序运行期间, ...
随机推荐
- 【MySQL】全量+增量的备份/恢复
生产环境中,有时需要做MySQL的备份和恢复工作.因MySQL是在运行过程中的,做全量备份需要时间,全量备份完成后又有数据变动,此时需要增量备份辅助.如果想恢复数据到一个空库(例如数据迁移或者上云等更 ...
- 全球首款iOS模拟器出炉!在违法的边缘疯狂试探
对于许多智能手机用户,特别是手游玩家来说,在手机屏幕的方寸之间进行操作显然并非特别方便,而且在多年之前,由于手机配置不足,也导致了用PC来玩手游的需求不断涌现.彼时,BlueStacks及夜神等一众A ...
- python如何永久添加模块搜索路径
win10系统 依次点击:控制面板\系统和安全\系统\高级系统设置\环境变量 找不到的话,直接在设置中搜索 环境变量 也一样 此时上面是用户变量 下面是系统变量 在系统变量中找到PYTHO ...
- D. Anton and Chess 模拟题 + 读题
http://codeforces.com/contest/734/problem/D 一开始的时候看不懂题目,以为象是中国象棋那样走,然后看不懂样例. 原来是走对角线的,长知识了. 所以我们就知道, ...
- 牛客网Java刷题知识点之什么是HTTP协议、什么是HTTP隧道、HTTP响应的结构是怎么样的、HTTP报头包含哪些、HTTP中GET与POST方法有什么区别
不多说,直接上干货! https://www.nowcoder.com/ta/review-java/review?tpId=31&tqId=21169&query=&asc= ...
- Mysql之Union用法
在数据库中,UNION和UNION ALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同. MYSQL中的UNION UNION在进行表链接后会筛选掉重复的记录,所以在表链接后 ...
- Docker与虚拟机
Docker与虚拟机 简述 Docker 在容器的基础上,进行了进一步的封装,从文件系统.网络互联到进程隔离等等,极大的简化了容器的创建和维护.使得 Docker 技术比虚拟机技术更为轻便.快捷.下面 ...
- 《高性能MySQL》读书笔记之创建高性能的索引
索引是存储引擎用于快速找到记录的一种数据结构.索引优化是对查询性能优化的最有效手段.索引能够轻易将查询性能提高几个数量级.创建一个最优的索引经常需要重写查询.5.1 索引基础 在MySQL中,存储引擎 ...
- CF1066E Binary Numbers AND Sum
思路: 模拟.实现: #include <iostream> using namespace std; ; ], b[]; ]; int main() { int n, m; while ...
- arcgis jsapi接口入门系列(4):用代码在地图画点线面
PS:用代码画点这样写是为了跟后面的用鼠标画点线面区分出来 画点 drawPointGraphic: function () { //点有多种样式:一般的点,显示文字,显示图片 //一般的点 let ...