闲言:
    这个月一直在学习NDIS驱动编程,杂七杂八的资料都看个遍了,做了点笔记,捋捋思路,发上来备忘。
    Ps:只是小菜的一点学习笔记,没什么技术含量,不过版主如果觉得对大家稍微有点帮助的话,嘿嘿,你懂的...

主题:
    由于NDIS架构本身相对比较复杂,有着比其他过滤驱动更多的"游戏规则"需要开发者耐心地学习和理解。所以NDIS驱动开发起来略显繁琐,不过也不必畏惧,多看多写,熟悉后便豁然开朗了。本文以寒江独钓的passthru为例,详细讲解开发一个基于NDIS中间层驱动的封包过滤驱动的原理和流程。由于笔者初学驱动,水平有限,失误之处还请大虾不啬赐教。

1.[前置知识]
    所有网络通信最终必须通过NDIS完成,NDIS横跨传输层/网络层/数据链路层。NDIS负责上下层驱动程序间服务原语与驱动程序入口之间的转换,分派消息通知。NDIS提供三个层次的接口:网络接口卡驱动程序(NIC)、中间层驱动程序(MD)和协议驱动程序。
    NIC: 下端直接控制网络接口卡硬件,上端提供接口,处理初始化网卡、停止网卡、发送和接受数据包。
    PD:  接收来自网卡或中间驱动程序的信息。
    MD:  网卡驱动和协议驱动之间的桥梁。插入自己的中间层驱动,从而可以截获网络封包,并重新进行封包、加密、网络地址转换、过滤和认证等操作。可以处理更低级的操作、功能强大,但是编程接口复杂,自动化安装困难,容易导致网络瘫痪。
    windows内核网络驱动的总体格局(从下至上):小端口驱动实现了对网卡硬件的驱动,包括对其中各个寄存器的访问、中断处理等。协议驱动将链路层、网络层和传输层集合在一起的一个驱动模块。上层是传输驱动界面(TDI)。TDI上层是用来实现Socket机制的AFD驱动模块。
(参见《内核情景分析》 第十章 第一小节)

2.[NDIS架构简析]
    这里对协议驱动和小端口驱动的流程理解都是参考WDK的相应代码总结出来的。详细的代码我就不贴了,节省版面,都是WDK上的原版代码,对一些具体的实现细节有兴趣的童鞋可以自己看看。一些核心函数可以参考ReactOS源码,能够更深层次地理解NDIS内部的结构。直接贴初始化流程图。将NDIS的流程和框架搞清楚了,再深究细节就不那么容易晕了。小菜我研究这玩意的时候,晕了无数次了。由于本文重点在于封包过滤,所以对于NDIS协议驱动和小端口驱动的初始化和收包发包过程仅做一些简单的梳理。核心部分还是在中间层驱动。
NDIS协议驱动  [初始化]
流程图来源思路DDK2000/Packet:

NDIS小端口驱动  [初始化]
NDIS_MINIPORT_CHARACTERISTICS –> NdisMInitializeWrapper -> NdisMRegisterMiniport
    关键部分就是填写小端口驱动特征,设置各种回调,编写NDIS驱动程序的本质就是处理各色各样的回调,有初始化的有卸载的,也还有响应中断进行收包发包的等等等等。编写回调虽是编写NDIS驱动的核心,但不在本节讨论范围。小端口初始化流程,详见下图:

NDIS收包发包的总体流程  [小端口+协议]
    收包由响应网卡产生的中断开始,发包由TDI传入协议驱动的IRP开始。

3.[封包过滤]
    原理:由于NDIS中间层驱动位于协议驱动程序和网络驱动程序之间,所以它可以看到发生在一个系统中的所有网络流量。而不是像协议驱动那样只能看到从网络到来的封包。中间层驱动用于封包过滤和防火墙开发再适合不过了。这里以寒江独钓中的passthru为例编写一个具有端口屏蔽的过滤驱动。

①中间层驱动的初始化  [passthru]
1.初始化小端口部分:
NdisMInitializeWrapper -> NDIS_MINIPORT_CHARACTERISTICS –> NdisIMRegisterLayeredMiniport
2.初始化协议驱动部分并关联至小端口:
NDIS_PROTOCOL_CHARACTERISTICS -> NdisRegisterProtocol -> NdisIMAssociateMiniport
3.passthru初始化总体结构

②封包结构分析与过滤  [TCP端口过滤]
    要对TCP端口进行过滤,很明显必须得先对TCP协议封包的结构进行分析。不同协议的封包格式都不尽相同,对封包格式有兴趣的童鞋可以使用wireshark多抓几个进行分析。如上图所示,寒江独钓中的例子中在每一个发包与收包的相关回调函数中都调用了AnalysisPacket函数对封包进行分析。如果需要添加我们的过滤规则的话,在这个中函数进行即可。TCP协议的封包总共有三个部分:第一是以太网部分,第二是IP部分(前两部分是所有协议的封包都有的),第三就是TCP部分了。
下面是使用wireshark抓到的TCP协议封包:

再来看看TCP HEADER的定义:

代码:
 typedef struct TCPv4_HEADER {
USHORT SourcePort; /* Source port */
USHORT DestinationPort; /* Destination port */
ULONG SequenceNumber; /* Sequence number */
ULONG AckNumber; /* Acknowledgement number */
UCHAR DataOffset; /* Data offset; 32-bit words (leftmost 4 bits) */
UCHAR Flags; /* Control bits (rightmost 6 bits) */
USHORT Window; /* Maximum acceptable receive window */
USHORT Checksum; /* Checksum of segment */
USHORT Urgent; /* Pointer to urgent data */
} TCPv4_HEADER, *PTCPv4_HEADER;

TCP HEADER的定位方式,定位到TCP Header并取得端口号,对需要过滤的端口返回STATUS_DRAP即可。搞端口转发稍微复杂点,不过除了修改端口和IP地址,也就是多了个计算校验和的问题。具体代码可以参考安全焦点上的《NAT在NDIS中间层驱动中的实现》。如果要对IP地址进行过滤的话也同样解析IP HEADER然后增加判断即可。

代码:
PTCPv4_HEADER pTCPHeader;
USHORT iphdrlen; iphdrlen = (pIPHeader->VIHL & 0x0f) * sizeof(ULONG);
pTCPHeader = (PTCPv4_HEADER)(pPacketContent+ + iphdrlen); //14是以太网包的固定长度+IP协议长度 //如果要对TCP的端口进行过滤的话,加个if语句判断下即可
DBGPRINT((" TCP 源端口: %u\n",ChangeHex(pTCPHeader->SourcePort)));
DBGPRINT((" TCP 目的端口:%u\n",ChangeHex(pTCPHeader->DestinationPort)));

③获取封包
    中间层驱动初始化完毕后,只需要在初始化时设置的相应回调函数进行处理即可。回调函数设置好了,封包的发送与接收都会调用我们的回调函数。过滤函数一般都在相应的收包发包函数内部的前端,如此一来可以提升处理效率。当然也省了不少麻烦(比如人家内存都分配好了,结果你要丢包)。不过也不死板,可以根据实际情况进行编写。对于发送出去的数据包处理,只要在PassThru中的MiniportSend中加入必要的操作代码,而对于接收的数据包时,则需要在ProtocolReceive和ProtocolReceviePackets中加入必要的操作代码,在这里,我们简单看一下相关的收包发包函数。
发送处理:

代码:
VOID
MPSendPackets(
IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
)
{
...
// 在为封包分配内存之前,调用我们的过滤函数
// 分配一个新的包描述符
NdisAllocatePacket(&Status,
&MyPacket,
pAdapt->SendPacketPoolHandle); if (Status == NDIS_STATUS_SUCCESS)
{
PSEND_RSVD SendRsvd; SendRsvd = (PSEND_RSVD)(MyPacket->ProtocolReserved);
// 把原来的包描述符保存在新分配的包描述符中的Reserved字段中
SendRsvd->OriginalPkt = Packet; // 获取包标识符
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); //处理缓冲区
NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet);
NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet); //将OOB数据结构从原封包中复制至新包中
NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket),
NDIS_OOB_DATA_FROM_PACKET(Packet),
sizeof(NDIS_PACKET_OOB_DATA)); // 复制中间层指定信息
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet,
&MediaSpecificInfo,
&MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize)
{
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket,
MediaSpecificInfo,
MediaSpecificInfoSize);
}
// 调用NdisSend发送数据
NdisSend(&Status,
pAdapt->BindingHandle,
MyPacket); if (Status != NDIS_STATUS_PENDING)
{
NdisFreePacket(MyPacket);
ADAPT_DECR_PENDING_SENDS(pAdapt);
}
...
}

接收处理:

代码:
INT
PtReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
)
{
...
#ifdef NDIS51
//
// Check if we can reuse the same packet for indicating up.
// See also: PtReceive().
//
(VOID)NdisIMGetCurrentPacketStack(Packet, &Remaining);
if (Remaining)
{
//
// We can reuse "Packet". Indicate it up and be done with it.
//
Status = NDIS_GET_PACKET_STATUS(Packet);
NdisMIndicateReceivePacket(pAdapt->MiniportHandle, &Packet, );
return((Status != NDIS_STATUS_RESOURCES) ? : );
}
#endif // NDIS51 // 从packet池中获取packet
NdisDprAllocatePacket(&Status,
&MyPacket,
pAdapt->RecvPacketPoolHandle);
...
// 设置封包flags
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); Status = NDIS_GET_PACKET_STATUS(Packet); NDIS_SET_PACKET_STATUS(MyPacket, Status);
NDIS_SET_PACKET_HEADER_SIZE(MyPacket, NDIS_GET_PACKET_HEADER_SIZE(Packet)); if (pAdapt->MiniportHandle != NULL)
{
// 调用NDIS库函数接受封包
NdisMIndicateReceivePacket(pAdapt->MiniportHandle, &MyPacket, );
}
...
}

④过滤规则的编写
    NDIS封包的过滤有很多种处理方式:延迟或重新排序、加密或解密、压缩或解压、包路由(NAT网络地址转换、LBFO负载平衡和失效替换)。如果修改了封包的内容的话,例如端口转发,需要重新修正校验和调整缓冲区和长度等相关信息。修改时需要注意字节顺序的问题。

示例驱动程序加载方式:控制面板 -> 网络连接 -> 本地连接 -> 属性 -> 安装 -> 服务 -> 添加 -> 从磁盘安装 -> 选择我们inf文件,忽略一切警告一路确定即可。
    示例驱动仅仅解析并打印TCP/IP的协议格式和封包数,如下图。更有趣的过滤规则大家自行探索吧!
    今天就讲到这吧,累挂了,希望能对和我一样的菜菜有所帮助吧。

passthru.zip.

科普NDIS封包过滤的更多相关文章

  1. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)

    catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...

  2. 基于Passthru的NDIS开发的个人理解

    这几天对NDIS的学习,基本思路是:首先熟悉理论知识→然后下载一个例子进行研究→最后例子自己模仿扩展→最最后尝试自己写一个新的. Passthru是微软NDIS自己写的一个框架驱动,NDIS开发者可以 ...

  3. iOS开发工具-网络封包分析工具Charles

    转自唐巧的技术博客:http://blog.devtang.com/blog/2013/12/11/network-tool-charles-intr/ Charles是在Mac下常用的截取网络封包的 ...

  4. iOS开发工具——网络封包分析工具Charles

    简介 Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析.Charles通过将自己设置成系统的网络访问代理服务器,使 ...

  5. 网络封包分析工具Charles使用

    网址:http://www.charlesproxy.com/ 截取网络封包的工具. 简介 Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯协议, ...

  6. 使用charles过滤网络请求

    1.对网络请求进行过滤,只监控向指定目录服务器上发送的请求 有以下方法: (1)在Structure视图或者Sequence视图的Filter 栏中填入需要过滤出来的关键字(适合临时性封包过滤) 或者 ...

  7. [转] iOS开发工具——网络封包分析工具Charles

    简介 Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析.Charles通过将自己设置成系统的网络访问代理服务器,使 ...

  8. WinPcap是用于网络封包抓取的一套工具

    WinPcap是用于网络封包抓取的一套工具,可适用于32位的操作平台上解析网络封包,包含了核心的封包过滤,一个底层动态链接库,和一个高层系统函数库,及可用来直接存取封包的应用程序界面. Winpcap ...

  9. 开发工具-网络封包分析工具Charles

    extends:http://blog.devtang.com/blog/2013/12/11/network-tool-charles-intr/ 简介 本文为InfoQ中文站特供稿件,首发地址为: ...

随机推荐

  1. Unity3d的批渲染 batch rendering

    http://blog.csdn.net/leonwei/article/details/41942157 批渲染(Batch) batch render 是大部分引擎提高渲染效率的方法,基本原理就是 ...

  2. 51nod1475(贪心&枚举)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1475 题意:中文题诶- 思路:看懂题意后,首先想到的是贪心: ...

  3. jQuery EasyUI/TopJUI实现数据表格的增删改查功能(不写js,纯HTML实现!!!)

    jQuery EasyUI/TopJUI实现数据表格的增删改查功能(不写js,纯HTML实现!!!) 废话不多说,直接贴上代码 <table id="configEdatagrid&q ...

  4. Git,SVN的优缺点及适合的范围,开源项目?公司项目?

    Git,SVN的优缺点及适合的范围,开源项目?公司项目? 使用git不久,粗浅理解: 1)适用对象不同.Git适用于参与开源项目的开发者.他们由于水平高,更在乎的是效率而不是易用性.Svn则不同,它适 ...

  5. AtCoder Regular Contest 078 D

    D - Fennec VS. Snuke Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement F ...

  6. Super Mario(线段树离线区间k值)

    以前见过这题,没做出来,知道是离线处理,这次仔细想了下, 首先把出现的高度都map离散化一下,以离散化出来的数目g建树,把每个位置都开俩个vector,一个存以这个位置为L的询问,一个存以这个位置为R ...

  7. 第十章 设计用户界面 之 构建UI布局

    1. 概述 本章内容包括:实现可在不同区域重用的片段.使用Razor模板设计和实现页面.设计可视结构的布局.基于模板页开发. 2. 主要内容 2.1 实现可在不同区域重用的片段 最简单的重用方式就是在 ...

  8. MySql数据库的相关操作

    SQL(Structred Query Language)结构化查询语言:和数据库交互的语言,进行数据库管理的语言. 一.数据库的操作: 1.查询所有数据库: show databases; 2.创建 ...

  9. bootstrap警告框、进度条和列表组

    警告框   <div class="container">      <div class="alert alert-success" rol ...

  10. Java编程基础-反射

    一.java反射 1.反射:动态获取类的信息,以及动态调用对象的方法的功能.可以理解为动态看透类的能力. 2.主要功能:在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意 ...