设为首页  
联系我们  
加入收藏  
网页制作 冲浪宝典 图形图像 操作系统 软件教学 编程开发 认证考试 安全技术 站长专区 文学驿站 娱乐天地 游戏天地 办公软件
文章搜索
您的位置: 首页 >> 文章首页 >> 编程开发 >> 其他开发语言 >> Filter-Hook Driver入门
精品推荐
其他开发语言点击TOP10
·数字小键盘指法练习
·用C语言编通讯录程序(初学者级别的)
·Modem 常用AT指令集
·单片机模拟I2C总线及24C02(I2C EEPROM)读写实例(源代码)
·C++经典电子书下载
·Thinking in C++ 简体中文第二版
·debug和release的区别
·error LNK2001: unresolved external symbol __ftol2 错误解决
·C库函数手册
·一个简单的C语言编译器
编程开发点击TOP10
·数字小键盘指法练习
·ASP.NET 程序中常用的三十三种代码
·用C语言编通讯录程序(初学者级别的)
·我写的Java学生成绩管理系统源代码
·CHK文件恢复工具
·Modem 常用AT指令集
·java笔试题
·异常java.sql.SQLException: Io exception:The Network Adapter could not establish connection
·单片机模拟I2C总线及24C02(I2C EEPROM)读写实例(源代码)
·C++经典电子书下载
精选专题

Filter-Hook Driver入门

作者: 来源:网络文章 时间:2005-12-17 20:56:29

Filter-Hook Driver入门(1)     ☆ Filter-Hook Driver
    ☆ PacketFilterExtensionPtr
    ☆ 设置/清除回调函数
        1) 调用IoGetDeviceObjectPointer()获取IpFilterDriver相应的设备对象
        2) 调用IoBuildDeviceIoControlRequest()构造IRP
        3) 调用IoCallDriver()向IpFilterDriver提交IRP
    ☆ 一个完整的Filter-Hook Driver框架(丢弃所有ICMP报文以及接收到的RST报文)
        1) ipflthookdrv.c
        2) dirs
        3) sources
        4) makefile
        5) installdriver.c
        6) ipflthookdrvtest.c
        7) 验证效果
    ☆ 参考资源

--------------------------------------------------------------------------

☆ Filter-Hook Driver

从Windows 2000开始IpFilterDriver是系统自带的一个驱动,顾名思义,就是IP过滤
驱动,对应ipfltdrv.sys文件。缺省情况下,这个驱动并未加载,但可以手工加载。

> sc queryex IpFilterDriver
SERVICE_NAME: IpFilterDriver
        TYPE               : 1  KERNEL_DRIVER
        STATE              : 1  STOPPED
                                (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0
        PID                : 0
        FLAGS              :
> enumservice.exe findstr /I ipfilter
设备驱动程序            停止     IpFilterDriver                 IP Traffic Filter Driver
> net start IpFilterDriver
The IP Traffic Filter Driver service was started sUCcessfully.
> enumservice.exe findstr /I ipfilter
设备驱动程序            运行     IpFilterDriver                 IP Traffic Filter Driver
> net stop IpFilterDriver
The IP Traffic Filter Driver service was stopped successfully.

Filter-Hook Driver是一种KMD,与IpFilterDriver相配合。显然这只能用于TCP/IP
协议,而对IPX/SPX、NetBEUI等其它协议无能为力。

最多只能安装一个Filter-Hook Driver。仅当filter-hook callback function为空
时,Filter-Hook Driver才能向IpFilterDriver注册自己所提供的回调函数,后者调
用filter-hook callback function以决定如何处理接收到的或者即将发送的IP报文。
注册回调函数结束之后,IpFilterDriver将Filter-Hook Driver相应的文件对象与回
调函数关联起来,以此确保只有一个Filter-Hook Driver可用。

从Windows XP开始,微软不推荐采用Filter-Hook Driver实现防火墙。从网络层次结
构上看,Filter-Hook Driver太高了。此外,Filter-Hook Driver还将干挠到ICS
其它个人防火墙。ICS即Internet Connection Sharing,可以简单理解成XP/2003
带的个人防火墙,当然事实上并非这么简单,ICS可以实现端口转发、NAT等类似功能。
如果你愿意,可以试着利用ICS将一台XP/2003配成NAT网关,我未实际测试,tk做过
一些实验。对于XP/2003,推荐采用NDIS Intermediate Driver实现防火墙。尽管如
此,我还是学习一下Filter-Hook Driver,因为它实现起来相对简单些。

用户态有一套Packet Filtering API,直接与IpFilterDriver打交道,做了适度优化,
可以根据srcIp、dstIp、srcPort、dstPort等等进行过滤,但与Filter-Hook Driver
无关!如果只关注IP地址和端口,可以考虑该套API,但试图过滤ICMP报文时就应考
虑Filter-Hook Driver。

☆ PacketFilterExtensionPtr

前面提到的回调函数的原型如下:

typedef  PF_FORWARD_ACTION ( *PacketFilterExtensionPtr )
(
    IN  unsigned char  *PacketHeader,
    IN  unsigned char  *Packet,
    IN  unsigned int    PacketLength,
    IN  unsigned int    RecvInterfaceIndex,
    IN  unsigned int    SendInterfaceIndex,
    IN  IPAddr          RecvLinkNextHop,
    IN  IPAddr          SendLinkNextHop
);

由于是回调函数,函数名是什么都无所谓,DDK文档建议起一个有意义的名字。该函
数有三种返回值:

PF_FORWARD

    直接交给IP协议栈处理。如果IP报文是发给本机的,延IP协议栈向上传输。如果
    IP报文是发给另一台主机的,并且本机使能了"IP转发(路由功能)",则根据路由
    表进行相应转发。

PF_DROP

    IP协议栈将丢弃该IP报文。

PF_PASS

    如果用户态Packet Filtering API定义了过滤规则,在此得到机会进行过滤。如
    果Filter-Hook Driver觉得应该给用户态Packet Filtering API一个机会,必须
    返回PF_PASS,此时由IpFilterDriver亲自过滤。返回PF_FORWARD意味着Packet
    Filtering API所定义的过滤规则失效,嘿嘿。

☆ 设置/清除回调函数

1) 调用IoGetDeviceObjectPointer()获取IpFilterDriver相应的设备对象

NTSTATUS IoGetDeviceObjectPointer
(
    IN  PUNICODE_STRING     ObjectName,
    IN  Access_MASK         DesiredAccess,
    OUT PFILE_OBJECT       *FileObject,
    OUT PDEVICE_OBJECT     *DeviceObject
);

ObjectName

    要对应DD_IPFLTRDRVR_DEVICE_NAME,这是pfhook.h中定义的宏,即
    L"\\Device\\IPFILTERDRIVER"。注意ObjectName的类型,需要调用
    RtlInitUnicodeString()。

DesiredAccess

    DDK文档中指出应指定"SYNCHRONIZE GENERIC_READ GENERIC_WRITE"。如果
    图省事,可以指定STANDARD_RIGHTS_ALL。

    参看Platform SDK DOC中ACCESS_MASK Reference。

FileObject

    卸载Filter-Hook Driver时,应调用ObDereferenceObject()减小这个文件对象
    的引用计数,此时将间接减小相应设备对象的引用计数,否则IpFilterDriver无
    法正确卸载。

    VOID ObDereferenceObject
    (
        IN  PVOID   Object
    );

DeviceObject

    后面将用到这个返回数据

2) 调用IoBuildDeviceIoControlRequest()构造IRP

PIRP IoBuildDeviceIoControlRequest
(
    IN  ULONG               IoControlCode,
    IN  PDEVICE_OBJECT      DeviceObject,
    IN  PVOID               InputBuffer     OPTIONAL,
    IN  ULONG               InputBufferLength,
    OUT PVOID               OutputBuffer    OPTIONAL,
    IN  ULONG               OutputBufferLength,
    IN  BOOLEAN             InternalDeviceIoControl,
    IN  PKEVENT             Event,
    OUT PIO_STATUS_BLOCK    IoStatusBlock
);

IoControlCode

    必须指定IOCTL_PF_SET_EXTENSION_POINTER

DeviceObject

    IpFilterDriver相应的设备对象

InputBuffer

    应该强制类型转换成PPF_SET_EXTENSION_HOOK_INFO,其ExtensionPointer成员
    等于回调函数地址。如果ExtensionPointer成员为NULL,意味着清除操作。

    typedef struct _PF_SET_EXTENSION_HOOK_INFO
    {
        PacketFilterExtensionPtr    ExtensionPointer;
    } PF_SET_EXTENSION_HOOK_INFO, *PPF_SET_EXTENSION_HOOK_INFO;

    卸载Filter-Hook Driver时,必须清除回调函数。

InputBufferLength

    sizeof( PF_SET_EXTENSION_HOOK_INFO )

OutputBuffer

    NULL

OutputBufferLength

    0

InternalDeviceIoControl

    必须指定成FALSE,使得IpFilterDriver处理IRP_MJ_DEVICE_CONTROL的Dispatch
    例程被调用。

Event

    NULL

IoStatusBlock

    应该指定一个有效值

3) 调用IoCallDriver()向IpFilterDriver提交IRP

NTSTATUS IoCallDriver
(
    IN      PDEVICE_OBJECT  DeviceObject,
    IN OUT  PIRP            Irp
);

DeviceObject

    IpFilterDriver相应的设备对象

Irp

    第2步构造的IRP

☆ 一个完整的Filter-Hook Driver框架(丢弃所有ICMP报文以及接收到的RST报文)

1) ipflthookdrv.c

本例可以丢弃所有ICMP报文以及接收到的RST报文。一般丢弃所有ICMP报文并不会造
成大的问题。显然,ping、tracert这类依赖ICMP报文的工具将无法正常使用,别人
也不能ping你,呵。根据th_flags丢弃所有发送到本机的RST报文,但允许本机发送
RST报文给别人。至于TCP/IP协议相关的更复杂的演示就不搞了,毕竟不是正经写防
火墙。

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7 & Windows DDK 2600.1106
* build -cZ -x86
*/

/************************************************************************
*                                                                      *
*                               Head File                              *
*                                                                      *
************************************************************************/

#include <ntddk.h>
/*
* typedef ULONG   IPAddr, IPMask;
*/
#include <ntddndis.h>
#include <pfhook.h>
/*
* 用到了CTL_CODE宏
*/
#include <devioctl.h>

/************************************************************************
*                                                                      *
*                               Macro                                  *
*                                                                      *
************************************************************************/

/*
* 后面要追加DeviceNumber
*/
#define INTERNALNAME                            L"\\Device\\IpFilterHookInternal"
/*
* DosDevices
*/
#define EXTERNALNAME                            L"\\??\\IpFilterHookExternal"
/*
* 参ExAllocatePoolWithTag第三形参的DDK文档
*/
#define PRIVATETAG                              'OFSN'
/*
* 0x0800是最小可用值
*/
#define IPFILTERHOOK_INDEX                      0x0800
#define IOCTL_IPFILTERHOOK_GET_FIREWALLFLAGS    CTL_CODE    \
(                                                           \
    FILE_DEVICE_NETWORK,                                    \
    IPFILTERHOOK_INDEX + 0,                                 \
    METHOD_BUFFERED,                                        \
    FILE_READ_ACCESS                                        \
)
#define IOCTL_IPFILTERHOOK_SET_FIREWALLFLAGS    CTL_CODE    \
(                                                           \
    FILE_DEVICE_NETWORK,                                    \
    IPFILTERHOOK_INDEX + 1,                                 \
    METHOD_BUFFERED,                                        \
    FILE_WRITE_ACCESS                                       \
)
/*
* 置位 - Enable
* 复位 - Disable
*/
#define FIREWALLFLAGS_DISABLEALL                0x00000000
/*
* 所有ICMP报文,包括接收、发送
*/
#define FIREWALLFLAGS_ALLICMP                   0x00000001
/*
* 接收到的RST报文
*/
#define FIREWALLFLAGS_INBOUNDRST                0x00000002
#define FIREWALLFLAGS_ENABLEALL                 0xFFFFFFFF
/*
* 缺省设置是全部允许,即不丢弃任何报文
*/
#define FIREWALLFLAGS_DEFAULT                   FIREWALLFLAGS_ENABLEALL

/*
* 设备扩展是自定义结构
*/
typedef struct _DEVICE_EXTENSION
{
    PDEVICE_OBJECT  DeviceObject;
    ULONG           DeviceNumber;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

#define OFFSETOF(TYPE, MEMBER)                  ((size_t)&((TYPE)0)->MEMBER)

/*
* Definitions for IP type of service (ip_tos)
*
* 重复定义是为了方便移植
*/
#define IPTOS_LOWDELAY                          0x10
#define IPTOS_THROUGHPUT                        0x08
#define IPTOS_RELIABILITY                       0x04
#define IPTOS_MINCOST                           0x02

#define IPPROTO_ICMP                            1
#define IPPROTO_TCP                             6
#define IPPROTO_UDP                             17

/*
* 自定义下面这些结构是为了方便移植
*/

#pragma pack( push, 1 )

/*
* Structure of an internet header, naked of options.
*/
struct ipheader
{
#ifdef LITTLE_ENDIAN
    /*
     * 0x45,LITTLE_ENDIAN序高位在后。ip_hl以4字节为单位。
     */
    unsigned char       ip_hl:4;    /* header length         */
    unsigned char       ip_v:4;     /* version               */
#else
    /*
     * 0x45,BIG_ENDIAN序高位在前
     */
    unsigned char       ip_v:4;     /* version               */
    unsigned char       ip_hl:4;    /* header length         */
#endif
    unsigned char       ip_tos;     /* type of service       */
    /*
     * ip_len以字节为单位
     */
    unsigned short int  ip_len;     /* total length          */
    unsigned short int  ip_id;      /* identification        */
    /*
     * ip_off以8字节为单位
     */
    unsigned short int  ip_off;     /* fragment offset field */
    /*
     * ip_off只占了后13-bits,前3-bits的后两位分别是DF、MF
     */
#define IP_DF 0x4000                /* dont fragment flag    */
#define IP_MF 0x2000                /* more fragments flag   */
    unsigned char       ip_ttl;     /* time to live          */
    unsigned char       ip_p;       /* protocol              */
    unsigned short int  ip_sum;     /* checksum              */
#if 0
    /*
     * 我们用不着这两个成员,为减少编译麻烦,注释掉它们
     */
    struct in_addr      ip_src;     /* source address        */
    struct in_addr      ip_dst;     /* dest address          */
#endif
};

/*
* TCP header. (with some options)
* Per RFC 793, September, 1981.
*/
struct tcpheader
{
    unsigned short int  th_sport;   /* source port            */
    unsigned short int  th_dport;   /* destination port       */
    unsigned int        th_seq;     /* sequence number        */
    unsigned int        th_ack;     /* acknowledgement number */
#ifdef LITTLEENDIAN
    /*
     * 0x50,LITTLEENDIAN序高位在后
     */
    unsigned char       th_x2:4,    /* (unused)               */
                        th_off:4;   /* data offset            */
#else
    /*
     * 0x50,BIGENDIAN序高位在前。th_off以4字节为单位。
     */
    unsigned char       th_off:4,   /* data offset            */
                        th_x2:4;    /* (unused)               */
#endif
    unsigned char       th_flags;
#define TH_FIN  0x01
#define TH_SYN  0x02
#define TH_RST  0x04
#define TH_PSH  0x08
#define TH_ACK  0x10
#define TH_URG  0x20
    unsigned short int  th_win;     /* window                 */
    unsigned short int  th_sum;     /* checksum               */
    unsigned short int  th_urp;     /* urgent pointer         */
    /*
     * 参看W.Richard Stevens的Tcp/Ip Illustrated Volume I 18.10小节
     *
     * kind=2 len=4 mss(不包括IP首部、TCP首部)
     *
     */
    unsigned char       th_optmsskind;
    unsigned char       th_optmsslen;
    unsigned short int  th_optmss;
    /*
     * kind=1
     */
    unsigned char       th_optnopa;
    unsigned char       th_optnopb;
    /*
     * kind=4 len=2
     */
    unsigned char       th_optsackpermittedkind;
    unsigned char       th_optsackpermittedlen;
};

#pragma pack( pop )

/************************************************************************
*                                                                      *
*                            Function Prototype                        *
*                                                                      *
************************************************************************/

/*
* 非Dispatch函数
*/
static NTSTATUS             IfhCreateDevice
(
    IN  PDRIVER_OBJECT          DriverObject,
    IN  ULONG                   DeviceNumber
);
/*
* 非Dispatch函数
*/
static VOID                 IfhDeleteDevice
(
    IN  PDEVICE_OBJECT          DeviceObject
);
/*
* 在DDK文档中搜索DispatchClose
*/
static NTSTATUS             IfhDispatchClose
(
    IN  PDEVICE_OBJECT          DeviceObject,
    IN  PIRP                    Irp
);
/*
* 在DDK文档中搜索DispatchCreate
*/
static NTSTATUS             IfhDispatchCreate
(
    IN  PDEVICE_OBJECT          DeviceObject,
    IN  PIRP                    Irp
);
/*
* 在DDK文档中搜索DispatchDeviceControl
*/
static NTSTATUS             IfhDispatchDeviceControl
(
    IN  PDEVICE_OBJECT          DeviceObject,
    IN  PIRP                    Irp
);
static NTSTATUS             IfhDoHook
(
    PacketFilterExtensionPtr    fwfunc
);
/*
* 非Dispatch函数,在DDK文档中搜索Unload
*/
static VOID                 IfhDriverUnload
(
    IN  PDRIVER_OBJECT  DriverObject
);
/*
* 回调函数,在DDK文档中搜索PacketFilterExtensionPtr
*/
static PF_FORWARD_ACTION    IfhFirewall
(
    IN  unsigned char          *PacketHeader,
    IN  unsigned char          *Packet,
    IN  unsigned int            PacketLength,
    IN  unsigned int            RecvInterfaceIndex,
    IN  unsigned int            SendInterfaceIndex,
    IN  IPAddr                  RecvLinkNextHop,
    IN  IPAddr                  SendLinkNextHop
);
/*
* 惟一的引出函数
*/
       NTSTATUS             DriverEntry
(
    IN  PDRIVER_OBJECT          DriverObject,
    IN  PUNICODE_STRING         RegistryPath
);

/*
* 参看<<The Windows 2000 Device Driver Book, Second Edition>>中5.2.6、
* 5.2.7小节。INIT、PAGE应该是大小写敏感的,可我居然看到过init、page,不清
* 楚怎么回事,稳妥起见还是用INIT、PAGE算了。
*
* IfhFirewall被调用时处在什么IRQL上,我也不清楚,安全起见,不放这里了。
*/
#ifdef ALLOC_PRAGMA

#pragma alloc_text( INIT, IfhCreateDevice           )
#pragma alloc_text( PAGE, IfhDeleteDevice           )
#pragma alloc_text( PAGE, IfhDispatchClose          )
#pragma alloc_text( PAGE, IfhDispatchCreate         )
#pragma alloc_text( PAGE, IfhDispatchDeviceControl  )
#pragma alloc_text( PAGE, IfhDoHook                 )
#pragma alloc_text( PAGE, IfhDriverUnload           )
#pragma alloc_text( INIT, DriverEntry               )

#endif

/************************************************************************
*                                                                      *
*                            Static Global Var                         *
*                                                                      *
************************************************************************/

/*
* 由于回调函数IfhFirewall要使用该标志,因此设为静态全局变量。
*/
static ULONG    FirewallFlags   = FIREWALLFLAGS_DEFAULT;

/************************************************************************/

/*
* 非Dispatch函数。如果不做错误处理,函数将精简很多,实际上就是调用两个函
* 数,IoCreateDevice与IoCreateSymbolicLink,分别建立内部设备名、外部设备
* 名。
*/
static NTSTATUS IfhCreateDevice
(
    IN  PDRIVER_OBJECT  DriverObject,
    IN  ULONG           DeviceNumber
)
{
    NTSTATUS            status;
    PDEVICE_OBJECT      DeviceObject;
    PDEVICE_EXTENSION   DeviceExtension;
    UNICODE_STRING      DeviceName;
    UNICODE_STRING      SymbolicLinkName;
    UNICODE_STRING      NumberUnicodeString;
    UNICODE_STRING      InternalNameUnicodeString;
    UNICODE_STRING      ExternalNameUnicodeString;

    KdPrint((  "Entering IfhCreateDevice()\n" ));
    /*
     * If the string is NULL-terminated, Length does not include the
     * trailing NULL.
     */
    NumberUnicodeString.Length              = 0;
    /*
     * 0xFFFFFFFF转换成10进制是4294967295,最长10个字符,加上结尾的NUL,不
     * 超过11。
     */
    NumberUnicodeString.MaximumLength       = 16;
    /*
     * PVOID ExAllocatePoolWithTag
     * (
     *     IN  POOL_TYPE   PoolType,
     *     IN  SIZE_T      NumberOfBytes,
     *     IN  ULONG       Tag
     * );
     *
     * 这里分配了内存,记得在后面释放它。
     */
    NumberUnicodeString.Buffer              = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        NumberUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == NumberUnicodeString.Buffer )
    {
        /*
         * DDK文档中指出,如果ExAllocatePool返回NULL,主调者应该返回
         * STATUS_INSUFFICIENT_RESOURCES。但是DDK文档中没有指出
         * ExAllocatePoolWithTag返回NULL时该如何,我只好类比一下,也返回
         * STATUS_INSUFFICIENT_RESOURCES。不知这里是否可以返回
         * STATUS_NO_MEMORY。
         */
        return( STATUS_INSUFFICIENT_RESOURCES );
    }
    /*
     * NTSTATUS RtlIntegerToUnicodeString
     * (
     *     IN      ULONG           Value,
     *     IN      ULONG           Base    OPTIONAL,
     *     IN OUT  PUNICODE_STRING String
     * );
     *
     * converts an unsigned integer value to a NULL-terminated string of one
     * or more Unicode characters in the specified base.
     */
    status                                  = RtlIntegerToUnicodeString
    (
        DeviceNumber,
        10,
        &NumberUnicodeString
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        return( status );
    }
    /*
     * VOID RtlInitUnicodeString
     * (
     *     IN OUT  PUNICODE_STRING DestinationString,
     *     IN      PCWSTR          SourceString
     * );
     *
     * 这个函数没有动态分配内存
     */
    RtlInitUnicodeString( &DeviceName, INTERNALNAME );
    /*
     * 在后面追加DeviceNumber。没有wsprintf()可用,不得已,只好这样变态地
     * 处理。
     */
    InternalNameUnicodeString.Length        = DeviceName.Length + NumberUnicodeString.Length;
    InternalNameUnicodeString.MaximumLength = InternalNameUnicodeString.Length + 2;
    InternalNameUnicodeString.Buffer        = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        InternalNameUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == InternalNameUnicodeString.Buffer )
    {
        /*
         * NTKERNELAPI VOID ExFreePoolWithTag
         * (
         *     IN  PVOID   P,
         *     IN  ULONG   Tag
         * );
         *
         * 需要释放NumberUnicodeString.Buffer
         */
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        /*
         * VOID RtlZeroMemory
         * (
         *     IN  VOID UNALIGNED *Destination,
         *     IN  SIZE_T          Length
         * );
         */
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        return( STATUS_INSUFFICIENT_RESOURCES );
    }
    /*
     * VOID RtlCopyUnicodeString
     * (
     *     IN OUT  PUNICODE_STRING DestinationString,
     *     IN      PUNICODE_STRING SourceString
     * );
     */
    RtlCopyUnicodeString
    (
        &InternalNameUnicodeString,
        &DeviceName
    );
    /*
     * NTSTATUS RtlAppendUnicodeStringToString
     * (
     *     IN OUT  PUNICODE_STRING Destination,
     *     IN      PUNICODE_STRING Source
     * );
     */
    status                                  = RtlAppendUnicodeStringToString
    (
        &InternalNameUnicodeString,
        &NumberUnicodeString
    );
    /*
     * 已经不需要NumberUnicodeString.Buffer,趁早释放它。
     */
    ExFreePoolWithTag
    (
        NumberUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &NumberUnicodeString,
        sizeof( NumberUnicodeString )
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        return( status );
    }
    InternalNameUnicodeString.Buffer[ InternalNameUnicodeString.Length / 2 ]
                                            = UNICODE_NULL;
    /*
     * NTSTATUS IoCreateDevice
     * (
     *     IN  PDRIVER_OBJECT      DriverObject,
     *     IN  ULONG               DeviceExtensionSize,
     *     IN  PUNICODE_STRING     DeviceName  OPTIONAL,
     *     IN  DEVICE_TYPE         DeviceType,
     *     IN  ULONG               DeviceCharacteristics,
     *     IN  BOOLEAN             Exclusive,
     *     OUT PDEVICE_OBJECT     *DeviceObject
     * );
     */
    status                                  = IoCreateDevice
    (
        DriverObject,
        sizeof( DEVICE_EXTENSION ),
        &InternalNameUnicodeString,
        FILE_DEVICE_NETWORK,
        0,
        FALSE,
        &DeviceObject
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        return( status );
    }
    DeviceObject->Flags                    = DO_BUFFERED_IO;
    /*
     * Initialize the Device Extension
     *
     * 设备扩展的内存空间是由IoCreateDevice给予的
     */
    DeviceExtension                         = ( PDEVICE_EXTENSION )DeviceObject->DeviceExtension;
    DeviceExtension->DeviceObject           = DeviceObject;
    DeviceExtension->DeviceNumber           = DeviceNumber;
    /*
     * 下面开始处理SymbolicLink
     */
    NumberUnicodeString.Length              = 0;
    NumberUnicodeString.MaximumLength       = 16;
    /*
     * 这里分配了内存,记得在后面释放它。
     */
    NumberUnicodeString.Buffer              = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        NumberUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == NumberUnicodeString.Buffer )
    {
        IoDeleteDevice( DeviceObject );
        DeviceObject    = NULL;
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        return( STATUS_INSUFFICIENT_RESOURCES );
    }
    /*
     * 一般内部设备号从0计,外部设备号从1计
     */
    status                                  = RtlIntegerToUnicodeString
    (
        DeviceNumber + 1,
        10,
        &NumberUnicodeString
    );
    if ( !NT_SUCCESS( status ) )
    {
        IoDeleteDevice( DeviceObject );
        DeviceObject    = NULL;
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        return( status );
    }
    RtlInitUnicodeString( &SymbolicLinkName, EXTERNALNAME );
    ExternalNameUnicodeString.Length        = SymbolicLinkName.Length + NumberUnicodeString.Length;
    ExternalNameUnicodeString.MaximumLength = ExternalNameUnicodeString.Length + 2;
    ExternalNameUnicodeString.Buffer        = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        ExternalNameUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == ExternalNameUnicodeString.Buffer )
    {
        /*
         * VOID IoDeleteDevice
         * (
         *     IN  PDEVICE_OBJECT  DeviceObject
         * );
         *
         * 需要抵消IoCreateDevice
         */
        IoDeleteDevice( DeviceObject );
        DeviceObject    = NULL;
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        return( STATUS_INSUFFICIENT_RESOURCES );
    }
    /*
     * VOID RtlCopyUnicodeString
     * (
     *     IN OUT  PUNICODE_STRING DestinationString,
     *     IN      PUNICODE_STRING SourceString
     * );
     */
    RtlCopyUnicodeString
    (
        &ExternalNameUnicodeString,
        &SymbolicLinkName
    );
    status                                  = RtlAppendUnicodeStringToString
    (
        &ExternalNameUnicodeString,
        &NumberUnicodeString
    );
    /*
     * 已经不需要NumberUnicodeString.Buffer,趁早释放它。
     */
    ExFreePoolWithTag
    (
        NumberUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &NumberUnicodeString,
        sizeof( NumberUnicodeString )
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            ExternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &ExternalNameUnicodeString,
            sizeof( ExternalNameUnicodeString )
        );
        IoDeleteDevice( DeviceObject );
        DeviceObject    = NULL;
        ExFreePoolWithTag
        (
            InternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &InternalNameUnicodeString,
            sizeof( InternalNameUnicodeString )
        );
        return( status );
    }
    ExternalNameUnicodeString.Buffer[ ExternalNameUnicodeString.Length / 2 ]
                                            = UNICODE_NULL;
    /*
     * NTSTATUS IoCreateSymbolicLink
     * (
     *     IN  PUNICODE_STRING SymbolicLinkName,
     *     IN  PUNICODE_STRING DeviceName
     * );
     */
    status                                  = IoCreateSymbolicLink
    (
        &ExternalNameUnicodeString,
        &InternalNameUnicodeString
    );
    /*
     * 已经不需要InternalNameUnicodeString.Buffer、ExternalNameUnicodeString.Buffer
     */
    ExFreePoolWithTag
    (
        ExternalNameUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &ExternalNameUnicodeString,
        sizeof( ExternalNameUnicodeString )
    );
    ExFreePoolWithTag
    (
        InternalNameUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &InternalNameUnicodeString,
        sizeof( InternalNameUnicodeString )
    );
    if ( !NT_SUCCESS( status ) )
    {
        IoDeleteDevice( DeviceObject );
        DeviceObject    = NULL;
    }
    return( STATUS_SUCCESS );
}  /* end of IfhCreateDevice */

/*
* 非Dispatch函数。实际上就是调用两个函数,IoDeleteSymbolicLink与
* IoDeleteDevice。
*/
static VOID IfhDeleteDevice
(
    IN  PDEVICE_OBJECT  DeviceObject
)
{
    NTSTATUS            status;
    PDEVICE_EXTENSION   DeviceExtension;
    UNICODE_STRING      SymbolicLinkName;
    UNICODE_STRING      NumberUnicodeString;
    UNICODE_STRING      ExternalNameUnicodeString;

    KdPrint((  "Entering IfhDeleteDevice()\n" ));
    DeviceExtension                         = ( PDEVICE_EXTENSION )DeviceObject->DeviceExtension;
    NumberUnicodeString.Length              = 0;
    NumberUnicodeString.MaximumLength       = 16;
    /*
     * 这里分配了内存,记得在后面释放它。
     */
    NumberUnicodeString.Buffer              = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        NumberUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == NumberUnicodeString.Buffer )
    {
        /*
         * 考虑输出一些调试信息
         *
         * This routine is defined in ntddk.h, wdm.h, and ndis.h.
         * A call to this macro requires double parentheses.
         */
        KdPrint((  "ExAllocatePoolWithTag() for NumberUnicodeString.Buffer failed\n" ));
        return;
    }
    /*
     * 一般内部设备号从0计,外部设备号从1计
     */
    status                                  = RtlIntegerToUnicodeString
    (
        DeviceExtension->DeviceNumber + 1,
        10,
        &NumberUnicodeString
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        KdPrint((  "RtlIntegerToUnicodeString() failed\n" ));
        return;
    }
    RtlInitUnicodeString( &SymbolicLinkName, EXTERNALNAME );
    ExternalNameUnicodeString.Length        = SymbolicLinkName.Length + NumberUnicodeString.Length;
    ExternalNameUnicodeString.MaximumLength = ExternalNameUnicodeString.Length + 2;
    ExternalNameUnicodeString.Buffer        = ( PWSTR )ExAllocatePoolWithTag
    (
        PagedPool,
        ExternalNameUnicodeString.MaximumLength,
        PRIVATETAG
    );
    if ( NULL == ExternalNameUnicodeString.Buffer )
    {
        ExFreePoolWithTag
        (
            NumberUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &NumberUnicodeString,
            sizeof( NumberUnicodeString )
        );
        KdPrint((  "ExAllocatePoolWithTag() for ExternalNameUnicodeString.Buffer failed\n" ));
        return;
    }
    /*
     * VOID RtlCopyUnicodeString
     * (
     *     IN OUT  PUNICODE_STRING DestinationString,
     *     IN      PUNICODE_STRING SourceString
     * );
     */
    RtlCopyUnicodeString
    (
        &ExternalNameUnicodeString,
        &SymbolicLinkName
    );
    status                                  = RtlAppendUnicodeStringToString
    (
        &ExternalNameUnicodeString,
        &NumberUnicodeString
    );
    /*
     * 已经不需要NumberUnicodeString.Buffer,趁早释放它。
     */
    ExFreePoolWithTag
    (
        NumberUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &NumberUnicodeString,
        sizeof( NumberUnicodeString )
    );
    if ( !NT_SUCCESS( status ) )
    {
        ExFreePoolWithTag
        (
            ExternalNameUnicodeString.Buffer,
            PRIVATETAG
        );
        RtlZeroMemory
        (
            &ExternalNameUnicodeString,
            sizeof( ExternalNameUnicodeString )
        );
        KdPrint((  "RtlAppendUnicodeStringToString() failed\n" ));
        return;
    }
    ExternalNameUnicodeString.Buffer[ ExternalNameUnicodeString.Length / 2 ]
                                            = UNICODE_NULL;
    /*
     * NTSTATUS IoDeleteSymbolicLink
     * (
     *     IN  PUNICODE_STRING SymbolicLinkName
     * );
     */
    status                                  = IoDeleteSymbolicLink
    (
        &ExternalNameUnicodeString
    );
    /*
     * 已经不需要ExternalNameUnicodeString.Buffer
     */
    ExFreePoolWithTag
    (
        ExternalNameUnicodeString.Buffer,
        PRIVATETAG
    );
    RtlZeroMemory
    (
        &ExternalNameUnicodeString,
        sizeof( ExternalNameUnicodeString )
    );
    if ( !NT_SUCCESS( status ) )
    {
        KdPrint((  "IoDeleteSymbolicLink() failed\n" ));
        return;
    }
    /*
     * VOID IoDeleteDevice
     * (
     *     IN  PDEVICE_OBJECT  DeviceObject
     * );
     */
    IoDeleteDevice( DeviceObject );
    return;
}  /* end of IfhDeleteDevice */

/*
* Handles call from Win32 CloseHandle request. For IpFilterHook driver, frees
* any buffer.
*/
static NTSTATUS IfhDispatchClose
(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
)
{
    KdPrint((  "Entering IfhDispatchClose()\n" ));
    Irp->IoStatus.Status        = STATUS_SUCCESS;
    Irp->IoStatus.Information   = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return( STATUS_SUCCESS );
}  /* end of IfhDispatchClose */

/*
* Handles call from Win32 CreateFile request. For IpFilterHook driver, does
* nothing.
*/
static NTSTATUS IfhDispatchCreate
(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
)
{
    KdPrint((  "Entering IfhDispatchCreate()\n" ));
    /*
     * 尽管IRP对于驱动是串行传输的,但是I/O管理器会并行使用IRP。一般而言,
     * Dispatch例程需要修改IRP中成员时,应该在栈上或设备扩展中建立一个副本。
     */
    Irp->IoStatus.Status        = STATUS_SUCCESS;
    /*
     * report that no bytes were transfered
     */
    Irp->IoStatus.Information   = 0;
    /*
     * VOID IoCompleteRequest
     * (
     *     IN  PIRP    Irp,
     *     IN  CCHAR   PriorityBoost
     * );
     *
     * IoCompleteRequest indicates the caller has completed all processing
     * for a given I/O request and is returning the given IRP to the I/O
     * Manager.
     *
     * PriorityBoost is IO_NO_INCREMENT if the original thread requested
     * an operation the driver could complete quickly or if the IRP is
     * completed with an error.
     *
     * Mark the IRP as "complete" - no further processing, no priority
     * increment.
     */
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    /*
     * 调用IoCompleteRequest()之后,I/O管理器可以从非分页池中释放IRP,因此
     * 不能出现return( Irp->IoStatus.Status )这样的代码。
     */
    return( STATUS_SUCCESS );
}  /* end of IfhDispatchCreate */

/*
* Handles call from Win32 DeviceIoControl request.
*/
static NTSTATUS IfhDispatchDeviceControl
(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
)
{
    NTSTATUS            status;
    PIO_STACK_LOCATION  IrpStackLocation;
    ULONG               IoControlCode;
    ULONG               InputBufferLength;
    ULONG               OutputBufferLength;
    ULONG               TransferSize;
    PVOID               TransferBuffer;

    KdPrint((  "Entering IfhDispatchDeviceControl()\n" ));
    status              = STATUS_SUCCESS;
    TransferSize        = 0;
    TransferBuffer      = Irp->AssociatedIrp.SystemBuffer;
    IrpStackLocation    = IoGetCurrentIrpStackLocation( Irp );
    IoControlCode       = IrpStackLocation->Parameters.DeviceIoControl.IoControlCode;
    InputBufferLength   = IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength;
    OutputBufferLength  = IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
    switch ( IoControlCode )
    {
    case IOCTL_IPFILTERHOOK_GET_FIREWALLFLAGS:
        if ( OutputBufferLength != sizeof( FirewallFlags ) )
        {
            status  = STATUS_INVALID_BUFFER_SIZE;
            break;
        }
        TransferSize    = OutputBufferLength;
        /*
         * 返回FirewallFlags
         */
        RtlCopyMemory
        (
            TransferBuffer,
            &FirewallFlags,
            TransferSize
        );
        break;
    case IOCTL_IPFILTERHOOK_SET_FIREWALLFLAGS:
        if ( InputBufferLength != sizeof( FirewallFlags ) )
        {
            status  = STATUS_INVALID_BUFFER_SIZE;
            break;
        }
        TransferSize    = InputBufferLength;
        /*
         * 设置FirewallFlags
         */
        RtlCopyMemory
        (
            &FirewallFlags,
            TransferBuffer,
            TransferSize
        );
        break;
    default:
        status          = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }
    /*
     * Now complete the IRP
     */
    Irp->IoStatus.Status                = status;
    Irp->IoStatus.Information           = TransferSize;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return( status );
}  /* end of IfhDispatchDeviceControl */

static NTSTATUS IfhDoHook ( PacketFilterExtensionPtr fwfunc )
{
    NTSTATUS                    status;
    UNICODE_STRING              DeviceName;
    PDEVICE_OBJECT              DeviceObject;
    PFILE_OBJECT                FileObject  = NULL;
    PIRP                        Irp         = NULL;
    PF_SET_EXTENSION_HOOK_INFO  hookinfo;
    IO_STATUS_BLOCK             IoStatusBlock;

    RtlInitUnicodeString( &DeviceName, DD_IPFLTRDRVR_DEVICE_NAME );
    /*
     * NTSTATUS IoGetDeviceObjectPointer
     * (
     *     IN  PUNICODE_STRING     ObjectName,
     *     IN  ACCESS_MASK         DesiredAccess,
     *     OUT PFILE_OBJECT       *FileObject,
     *     OUT PDEVICE_OBJECT     *DeviceObject
     * );
     */
    status                      = IoGetDeviceObjectPointer
    (
        &DeviceName,
        SYNCHRONIZE GENERIC_READ GENERIC_WRITE,
        &FileObject,
        &DeviceObject
    );
    if ( !NT_SUCCESS( status ) )
    {
        KdPrint((  "IoGetDeviceObjectPointer() failed\n" ));
        goto IfhDoHook_exit;
    }
    hookinfo.ExtensionPointer   = fwfunc;
    /*
     * PIRP IoBuildDeviceIoControlRequest
     * (
     *     IN  ULONG               IoControlCode,
     *     IN  PDEVICE_OBJECT      DeviceObject,
     *     IN  PVOID               InputBuffer     OPTIONAL,
     *     IN  ULONG               InputBufferLength,
     *     OUT PVOID               OutputBuffer    OPTIONAL,
     *     IN  ULONG               OutputBufferLength,
     *     IN  BOOLEAN             InternalDeviceIoControl,
     *     IN  PKEVENT             Event,
     *     OUT PIO_STATUS_BLOCK    IoStatusBlock
     * );
     */
    Irp                         = IoBuildDeviceIoControlRequest
    (
        IOCTL_PF_SET_EXTENSION_POINTER,
        DeviceObject,
        &hookinfo,
        sizeof( hookinfo ),
        NULL,
        0,
        FALSE,
        NULL,
        &IoStatusBlock
    );
    if ( NULL == Irp )
    {
        status  = IoStatusBlock.Status;
        KdPrint((  "IoBuildDeviceIoControlRequest() failed\n" ));
        goto IfhDoHook_exit;
    }
    /*
     * NTSTATUS IoCallDriver
     * (
     *     IN      PDEVICE_OBJECT  DeviceObject,
     *     IN OUT  PIRP            Irp
     * );
     */
    status                      = IoCallDriver
    (
        DeviceObject,
        Irp
    );
    if ( !NT_SUCCESS( status ) )
    {
        KdPrint((  "IoCallDriver() failed\n" ));
    }
    /*
     * IRPs created using IoBuildDeviceIoControlRequest must be completed
     * by calling IoCompleteRequest and not by merely deallocating the IRP
     * with IoFreeIrp.
     */
    Irp                         = NULL;

IfhDoHook_exit:

    if ( NULL != FileObject )
    {
        ObDereferenceObject( FileObject );
        FileObject  = NULL;
    }
    return( status );
}  /* end of IfhDoHook */

static VOID IfhDriverUnload
(
    IN  PDRIVER_OBJECT  DriverObject
)
{
    PDEVICE_OBJECT      NextDeviceObject;
    PDEVICE_EXTENSION   DeviceExtension;

    KdPrint((  "Entering IfhDriverUnload()\n" ));
    /*
     * Loop through each device controlled by driver
     */
    NextDeviceObject    = DriverObject->DeviceObject;
    /*
     * 这是个单向非循环链表
     */
    while ( NextDeviceObject )
    {
        /*
         * Dig out the Device Extension from the Device Object
         */
        DeviceExtension     = ( PDEVICE_EXTENSION )NextDeviceObject->DeviceExtension;
        NextDeviceObject    = NextDeviceObject->NextDevice;
        IfhDeleteDevice( DeviceExtension->DeviceObject );
    }  /* end of while */
    /*
     * 清除回调函数
     */
    if ( !NT_SUCCESS( IfhDoHook( NULL ) ) )
    {
        KdPrint((  "IfhDoHook( NULL ) failed\n" ));
    }
    return;
}  /* end of IfhDriverUnload */

/*
* 不清楚流程到达这个回调函数的时候前面是否已经做过一些检查,安全起见还是
* 多做一些检查为妙。
*/
static PF_FORWARD_ACTION IfhFirewall
(
    IN  unsigned char  *PacketHeader,
    IN  unsigned char  *Packet,
    /*
     * This size does not include the size of the IP header
     */
    IN  unsigned int    PacketLength,
    IN  unsigned int    RecvInterfaceIndex,
    IN  unsigned int    SendInterfaceIndex,
    IN  IPAddr          RecvLinkNextHop,
    IN  IPAddr          SendLinkNextHop
)
{
    /*
     * 原则是,如未明确禁止就允许,并且给用户态Packet Filtering API一个机
     * 会。
     */
    PF_FORWARD_ACTION   action  = PF_PASS;
    /*
     * IP首部数据肯定到位了
     */
    struct ipheader    *iph     = ( struct ipheader * )PacketHeader;
    struct tcpheader   *tcph    = ( struct tcpheader * )Packet;

    if
    (
        ( IPPROTO_ICMP == iph->ip_p ) &&
        ( !( FirewallFlags & FIREWALLFLAGS_ALLICMP ) )
    )
    {
        /*
         * 丢弃所有ICMP报文
         */
        action  = PF_DROP;
        goto IfhFirewall_exit;
    }
    /*
     * For received packets, SendInterfaceIndex is set to INVALID_PF_IF_INDEX
     */
    if
    (
        ( IPPROTO_TCP == iph->ip_p ) &&
        ( INVALID_PF_IF_INDEX == SendInterfaceIndex ) &&
        ( !( FirewallFlags & FIREWALLFLAGS_INBOUNDRST ) )
    )
    {
        /*
         * 丢弃所有接收到的RST报文。
         *
         * 检查是否是有效的TCP报文,考虑动用raw socket发送的报文。
         */
        if ( PacketLength >= OFFSETOF( struct tcpheader *, th_win ) )
        {
            if ( tcph->th_flags & TH_RST )
            {
                action  = PF_DROP;
            }
        }
    }

IfhFirewall_exit:

    return( action );
}  /* end of IfhFirewall */

/*
* DriverEntry is the first routine called after a driver is loaded, and
* is responsible for initializing the driver.
*
* you'll find a list of NTSTATUS status codes in the DDK header
* ntstatus.h (\WINDDK\2600.1106\inc\ddk\wxp\)
*/
NTSTATUS DriverEntry
(
    IN  PDRIVER_OBJECT  DriverObject,
    IN  PUNICODE_STRING RegistryPath
)
{
    NTSTATUS    status;

    /*
     * kernel-mode functions and the functions in your driver use the
     * __stdcall calling convention when compiled for an x86 computer.
     * This shouldn't affect any of your programming, but it's something
     * to bear in mind when you're debugging
     *
     * This routine has no effect if compiled in a free build environment.
     * You should compiled in a checked build environment.
     */
    KdPrint((  "Entering DriverEntry()\n" ));

    /*
     * If this driver controlled real hardware, code would be placed here
     * to locate it. Using IoReportDetectedDevice, the ports, IRQs, and
     * DMA channels would be "marked" as "in use" and under the control of
     * this driver. This IpFilterHook driver has no HW, so...
     */

    /*
     * Announce other driver entry points
     */
    DriverObject->DriverUnload                              = IfhDriverUnload;
    /*
     * This includes Dispatch routines for Create, Write & Read
     */
    DriverObject->MajorFunction[IRP_MJ_CREATE           ]   = IfhDispatchCreate;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL   ]   = IfhDispatchDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_CLOSE            ]   = IfhDispatchClose;
    /*
     * 参<<Programming the Microsoft Windows Driver Model, 2nd Ed>>第三章
     * 中"Error Handling"小节。
     *
     * Which Exceptions Can Be Trapped
     *
     * Gary Nebbett researched the question of which exceptions can be
     * trapped with the structured exception mechanism and reported his
     * results in a newsgroup post several years ago. In summary, the
     * following exceptions will be caught when they occur at IRQL less
     * than or equal to DISPATCH_LEVEL (note that some of these are
     * specific to the Intel x86 processor):
     *
     * a. Anything signaled by ExRaiseStatus and related functions
     * b. Attempt to dereference invalid pointer to user-mode memory
     * c. Debug or breakpoint exception
     * d. Integer overflow (INTO instruction)
     * e. Invalid opcode
     *
     * Note that a reference to an invalid kernel-mode pointer leads
     * directly to a bug check and can’t be trapped. Likewise, a
     * divide-by-zero exception or a BOUND instruction exception leads to
     * a bug check.
     *
     * 稳妥起见,还是使用SEH机制吧,尽量避免调试时重启。
     */
    __try
    {
        KdPrint((  "You should see this message [0]\n" ));
        /*
         * 设置回调函数
         */
        status  = IfhDoHook( IfhFirewall );
        if ( !NT_SUCCESS( status ) )
        {
            KdPrint((  "IfhDoHook( IfhFirewall ) failed\n" ));
        }
        else
        {
            /*
             * For each physical or logical device detected that will be under
             * this Driver's control, a new Device Object must be created.
             *
             * This call would be repeated until all devices are created.
             *
             * 我们这里只创建了一个设备对象
             */
            status  = IfhCreateDevice
            (
                DriverObject,
                0
            );
        }
        KdPrint((  "You should see this message [1]\n" ));
    }
    __except ( EXCEPTION_EXECUTE_HANDLER )
    {
        KdPrint((  "__except{}\n" ));
        status  = STATUS_UNSUCCESSFUL;
    }
    KdPrint((  "Exiting DriverEntry()\n" ));
    return( status );
}  /* end of DriverEntry */

/************************************************************************/

--------------------------------------------------------------------------

2) dirs

DIRS=code

3) sources

--------------------------------------------------------------------------
#
# Use the TARGETNAME macro to specify the name of the library to be built.
# Do not include the file name extension
#
TARGETNAME=ipflthookdrv

#
# All build products (such as .exe, .dll, and .lib files) will be placed
# in this directory
#
# BUILD_ALT_DIR的值会被追加在TARGETPATH之后,如果你嫌BUILD_ALT_DIR太碍眼,
# 可以删除该环境变量。
#
TARGETPATH=obj

#
# Use the TARGETTYPE macro to specify the type of product being built.
# TARGETTYPE gives the Build utility clues about some of the input files
# that it should expect. You must include this macro in your sources file.
#
TARGETTYPE=DRIVER

#
# Use the USE_PDB macro if your debug symbolic files will use a VC4 PDB.
# This is the default in the Windows XP build environment.
#
USE_PDB=1

#
# Use the INCLUDES macro to indicate the location of the headers to be
# included in your build
#
INCLUDES=

#
# Use the MSC_WARNING_LEVEL macro to set the warning level to use on the
# compiler. The default is /W3.
#
# After your code