"Filter-Hook Driver"入门

    ☆ 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 builds without errors, you might want to change
# MSC_WARNING_LEVEL to /W3 /WX. Setting this value causes warnings to show
# as errors.
#
MSC_WARNING_LEVEL=-W3 -WX

#
# The SOURCES macro specifies the files to be compiled. The SOURCES macro
# is required by the Build utility. This macro must be placed in your
# sources file. All files specified by this macro must reside in the
# directory containing the sources file.
#
SOURCES=ipflthookdrv.c
--------------------------------------------------------------------------

4) makefile

!INCLUDE $(NTMAKEENV)/makefile.def

假设进入了"Win XP Checked Build Environment":

J:/source/driver/ipflthookdrv> set BUILD_ALT_DIR
BUILD_ALT_DIR=chk_wxp_x86
J:/source/driver/ipflthookdrv> tree /f /a
ipflthookdrv
|   dirs
|
/---code
        installdriver.c
        ipflthookdrv.c
        ipflthookdrvtest.c
        makefile
        sources
J:/source/driver/ipflthookdrv> build -cZ -x86

5) installdriver.c

IpFilterHook要依赖IpFilterDriver,为了方便地安装IpFilterHook,需要修改一下
installdriver.c。就是调用CreateService()时指定依赖关系。

lpDependencies

    指向形如"AAAA/0BBBB/0CCCC/0/0"的缓冲区。为了简化installdriver.c的处理,
    要求[-p dependencies]中dependencies形如"AAAA,BBBB,CCCC,/0"。

--------------------------------------------------------------------------
/*
* Version  : 1.10
* Compile  : For x86/EWindows XP SP1 & VC 7
*          : cl installdriver.c /nologo /Os /G6 /Gz /Gs65536 /W3 /WX /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
*          :
* Create   : 2003-12-08 16:47
* Modify   : 2004-07-15 18:26
*/

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

/*
* #define _WIN32_WINNT 0x0501
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

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

#pragma comment( linker, "/INCREMENTAL:NO"    )
#pragma comment( linker, "/subsystem:console" )
#pragma comment( lib,    "kernel32.lib"       )
#pragma comment( lib,    "advapi32.lib"       )

#define VERSION "1.10"

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

static void PrintWin32ErrorCLI
(
    char   *message,
    DWORD   dwMessageId
);
static void usage
(
    char   *arg
);

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

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

static void PrintWin32ErrorCLI ( char *message, DWORD dwMessageId )
{
    char   *errMsg;

    FormatMessage
    (
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dwMessageId,
        MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
        ( LPTSTR )&errMsg,
        0,
        NULL
    );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32ErrorCLI */

static void usage ( char *arg )
{
    fprintf
    (
        stderr,
        "Usage: %s [-h] [-v] [-t target] [-s servicename] [-d displayname]/n"
        "       [-c cmdline] [-p dependencies]/n",
        arg
    );
    exit( EXIT_FAILURE );
}  /* end of usage */

int __cdecl main ( int argc, char * argv[] )
{
    SC_HANDLE       scm             = ( SC_HANDLE )NULL,
                    sc_handle       = ( SC_HANDLE )NULL;
    unsigned char  *target          = NULL,
                   *servicename     = NULL,
                   *displayname     = NULL,
                   *cmdline         = NULL,
                   *dependencies    = NULL,
                   *p;
    int             c,
                    ret             = EXIT_FAILURE;

    if ( 1 == argc )
    {
        usage( argv[0] );
    }
    /*
     * 从argv[1]开始循环处理命令行参数
     */
    for ( c = 1; c < argc; c++ )
    {
        /*
         * 同时支持-和/两种引入命令行参数的方式
         */
        if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
        {
            usage( argv[0] );
        }
        else
        {
            /*
             * 在这个字节上,大小写不敏感
             */
            switch ( tolower( argv[c][1] ) )
            {
            case 'c':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                cmdline         = argv[++c];
                break;
            case 'd':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                displayname     = argv[++c];
                break;
            case 'p':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                dependencies    = argv[++c];
                break;
            case 's':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                servicename     = argv[++c];
                break;
            case 't':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                target          = argv[++c];
                break;
            case 'v':
                fprintf( stderr, "%s ver "VERSION"/n", argv[0] );
                return( EXIT_SUCCESS );
            case 'h':
            case '?':
            default:
                usage( argv[0] );
                break;
            }  /* end of switch */
        }
    }  /* end of for */
    /*
     * 检查参数有效性
     */
    if ( NULL == servicename )
    {
        fprintf( stderr, "Checking your [-s servicename]/n" );
        return( EXIT_FAILURE );
    }
    if ( NULL == displayname )
    {
        fprintf( stderr, "Checking your [-d displayname]/n" );
        return( EXIT_FAILURE );
    }
    if ( NULL == cmdline )
    {
        fprintf( stderr, "Checking your [-c cmdline]/n" );
        return( EXIT_FAILURE );
    }
    /*
     * Header : Declared in Winsvc.h; include Windows.h.
     * Library: Use Advapi32.lib.
     *
     * SC_HANDLE OpenSCManager
     * (
     *     LPCTSTR lpMachineName,      // computer name
     *     LPCTSTR lpDatabaseName,     // SCM database name
     *     DWORD   dwDesiredAccess     // access type
     * );
     *
     * 第一形参可以用target,也可用//<target>。还应该尝试unicodeserver。
     */
    scm         = OpenSCManager
    (
        target,
        SERVICES_ACTIVE_DATABASE,
        SC_MANAGER_CREATE_SERVICE
    );
    if ( NULL == scm )
    {
        PrintWin32ErrorCLI( "OpenSCManager() failed", GetLastError() );
        goto main_exit;
    }
    /*
     * 要求用逗号做每个服务名的结尾
     */
    if ( NULL != dependencies )
    {
        p   = dependencies;
        while ( *p )
        {
            if ( ',' == *p )
            {
                *p  = '/0';
            }
            p++;
        }  /* end of while */
    }
    /*
     * SC_HANDLE CreateService
     * (
     *     SC_HANDLE   hSCManager,         // handle to SCM database
     *     LPCTSTR     lpServiceName,      // name of service to start
     *     LPCTSTR     lpDisplayName,      // display name
     *     DWORD       dwDesiredAccess,    // type of access to service
     *     DWORD       dwServiceType,      // type of service
     *     DWORD       dwStartType,        // when to start service
     *     DWORD       dwErrorControl,     // severity of service failure
     *     LPCTSTR     lpBinaryPathName,   // name of binary file
     *     LPCTSTR     lpLoadOrderGroup,   // name of load ordering group
     *     LPDWORD     lpdwTagId,          // tag identifier
     *     LPCTSTR     lpDependencies,     // array of dependency names
     *     LPCTSTR     lpServiceStartName, // account name
     *     LPCTSTR     lpPassword          // account password
     * );
     */
    sc_handle   = CreateService
    (
        scm,
        servicename,
        displayname,
        SERVICE_ALL_ACCESS,
        SERVICE_KERNEL_DRIVER,
        SERVICE_DEMAND_START,
        SERVICE_ERROR_NORMAL,
        cmdline,
        NULL,
        NULL,
        dependencies,
        NULL,
        NULL
    );
    if ( NULL == sc_handle )
    {
        PrintWin32ErrorCLI( "CreateService() failed", GetLastError() );
        goto main_exit;
    }
    ret         = EXIT_SUCCESS;

main_exit:

    if ( NULL != sc_handle )
    {
        CloseServiceHandle( sc_handle );
        sc_handle = ( SC_HANDLE )NULL;
    }
    if ( NULL != scm )
    {
        CloseServiceHandle( scm );
        scm = ( SC_HANDLE )NULL;
    }
    return( ret );
}  /* end of main */

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

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

使用举例:

> installdriver.exe -s IpFilterHook -d IpFilterHook -c c:/onlytemp/ipflthookdrv.sys -p IpFilterDriver,

这次安装与安装loopback.sys相比,就是多了依赖关系:

--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/IpFilterHook]
"DependOnService"=hex(7):49,00,70,00,46,00,69,00,6c,00,74,00,65,00,72,00,44,00,/
  72,00,69,00,76,00,65,00,72,00,00,00,00,00
"DependOnGroup"=hex(7):00,00
--------------------------------------------------------------------------

6) ipflthookdrvtest.c

这个程序用于设置回调函数用到的标志FirewallFlags,并显示设置前后该标志的值。

丢弃所有ICMP报文,包括接收、发送两种情形,丢弃接收到的RST报文

    ipflthookdrvtest.exe -f 0

不做任何过滤

    ipflthookdrvtest.exe

只丢弃所有ICMP报文

    ipflthookdrvtest.exe -f 0xFFFFFFFE

只丢弃接收到的RST报文

    ipflthookdrvtest.exe -f 0xFFFFFFFD

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7
* cl ipflthookdrvtest.c /nologo /Os /G6 /Gz /Gs65536 /W3 /WX /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
*/

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

/*
* #define _WIN32_WINNT 0x0501
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/*
* 用到了CTL_CODE宏
*/
#include <winioctl.h>

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

#pragma comment( linker, "/INCREMENTAL:NO"    )
#pragma comment( linker, "/subsystem:console" )

#define VERSION                                 "1.00"
#define EXTERNALNAME                            ".//IpFilterHookExternal1"

/*
* 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

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

static void PrintWin32ErrorCLI
(
    char   *message,
    DWORD   dwMessageId
);
static void usage
(
    char   *arg
);

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

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

static void PrintWin32ErrorCLI ( char *message, DWORD dwMessageId )
{
    char   *errMsg;

    FormatMessage
    (
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dwMessageId,
        MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
        ( LPTSTR )&errMsg,
        0,
        NULL
    );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32ErrorCLI */

static void usage ( char *arg )
{
    fprintf
    (
        stderr,
        "Usage: %s [-h] [-v] [-f FirewallFlags]/n",
        arg
    );
    exit( EXIT_FAILURE );
}  /* end of usage */

int __cdecl main ( int argc, char * argv[] )
{
    int     c,
            ret                 = EXIT_FAILURE;
    HANDLE  Device              = INVALID_HANDLE_VALUE;
    ULONG   FirewallFlags       = FIREWALLFLAGS_DEFAULT;
    ULONG   OrigFirewallFlags;
    DWORD   BytesReturned;

#if 0
    if ( 1 == argc )
    {
        usage( argv[0] );
    }
#endif
    /*
     * 从argv[1]开始循环处理命令行参数
     */
    for ( c = 1; c < argc; c++ )
    {
        /*
         * 同时支持-和/两种引入命令行参数的方式
         */
        if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
        {
            usage( argv[0] );
        }
        else
        {
            /*
             * 在这个字节上,大小写不敏感
             */
            switch ( tolower( argv[c][1] ) )
            {
            case 'f':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                FirewallFlags   = strtoul( argv[++c], NULL, 0 );
                break;
            case 'v':
                fprintf( stderr, "%s ver "VERSION"/n", argv[0] );
                return( EXIT_SUCCESS );
            case 'h':
            case '?':
            default:
                usage( argv[0] );
                break;
            }  /* end of switch */
        }
    }  /* end of for */
    /*
     * HANDLE CreateFile
     * (
     *     LPCTSTR                 lpFileName,             // file name
     *     DWORD                   dwDesiredAccess,        // access mode
     *     DWORD                   dwShareMode,            // share mode
     *     LPSECURITY_ATTRIBUTES   lpSecurityAttributes,   // SD
     *     DWORD                   dwCreationDisposition,  // how to create
     *     DWORD                   dwFlagsAndAttributes,   // file attributes
     *     HANDLE                  hTemplateFile           // handle to template file
     * );
     *
     * If the function fails, the return value is INVALID_HANDLE_VALUE. To
     * get extended error information, call GetLastError.
     */
    Device  = CreateFile
    (
        EXTERNALNAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,           // If lpSecurityAttributes is NULL, the handle cannot be inherited
        OPEN_EXISTING,  // The function fails if the file does not exist
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if ( INVALID_HANDLE_VALUE == Device )
    {
        PrintWin32ErrorCLI( "CreateFile() failed", GetLastError() );
        goto main_exit;
    }
    /*
     * BOOL DeviceIoControl
     * (
     *     HANDLE          hDevice,
     *     DWORD           dwIoControlCode,
     *     LPVOID          lpInBuffer,
     *     DWORD           nInBufferSize,
     *     LPVOID          lpOutBuffer,
     *     DWORD           nOutBufferSize,
     *     LPDWORD         lpBytesReturned,
     *     LPOVERLAPPED    lpOverlapped
     * );
     */
    if
    (
        FALSE == DeviceIoControl
        (
            Device,
            IOCTL_IPFILTERHOOK_GET_FIREWALLFLAGS,
            NULL,
            0,
            &OrigFirewallFlags,
            sizeof( OrigFirewallFlags ),
            &BytesReturned,
            NULL
        )
    )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( GET ) for OrigFirewallFlags failed", GetLastError() );
        goto main_exit;
    }
    printf( "OrigFirewallFlags   = 0x%08X/n", OrigFirewallFlags );
    if
    (
        FALSE == DeviceIoControl
        (
            Device,
            IOCTL_IPFILTERHOOK_SET_FIREWALLFLAGS,
            &FirewallFlags,
            sizeof( FirewallFlags ),
            NULL,
            0,
            &BytesReturned,
            NULL
        )
    )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( SET ) failed", GetLastError() );
        goto main_exit;
    }
    if
    (
        FALSE == DeviceIoControl
        (
            Device,
            IOCTL_IPFILTERHOOK_GET_FIREWALLFLAGS,
            NULL,
            0,
            &FirewallFlags,
            sizeof( FirewallFlags ),
            &BytesReturned,
            NULL
        )
    )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( GET ) for FirewallFlags failed", GetLastError() );
        goto main_exit;
    }
    printf( "FirewallFlags       = 0x%08X/n", FirewallFlags );
    ret     = EXIT_SUCCESS;

main_exit:

    if ( INVALID_HANDLE_VALUE != Device )
    {
        CloseHandle( Device );
        Device  = INVALID_HANDLE_VALUE;
    }
    return( ret );
}  /* end of main */

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

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

7) 验证效果

将ipflthookdrv.c、ipflthookdrv.pdb、ipflthookdrv.sys复制到VMware Guest上同
一目录下,比如onlytemp下。用installdriver.exe安装ipflthookdrv.sys。

installdriver.exe -s IpFilterHook -d IpFilterHook -c c:/onlytemp/ipflthookdrv.sys -p IpFilterDriver,

由于IpFilterHook依赖于IpFilterDriver,这次应该指定依赖关系,否则测试起来总
要事先手工启动IpFilterDriver。现在指定了依赖关系,可直接启动IpFilterHook,
系统会检查依赖关系并自动启动IpFilterDriver。

如果需要用SoftICE对ipflthookdrv.sys进行源码级调试,请使用如下命令:

nmsym.exe /translate:always,source,package /output:ipflthookdrv.nms ipflthookdrv.sys
nmsym.exe /symload:ipflthookdrv.nms
nmsym.exe /unload:ipflthookdrv.nms

这次我运气好,没有崩溃,一次性搞定。一般来讲,测试驱动前应该用sync.exe工具
刷新磁盘缓冲区,防止数据丢失、损坏硬盘等等。

> sc queryex IpFilterHook
SERVICE_NAME: IpFilterHook
        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              :
> 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              :
> sc EnumDepend IpFilterDriver (靠,这个命令居然是枚举谁依赖IpFilterDriver)
Enum: entriesRead  = 1

SERVICE_NAME: IpFilterHook
DISPLAY_NAME: IpFilterHook
        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
> net start IpFilterHook
The IpFilterHook service was started successfully.

下面介绍一下如何验证防火墙起作用了:

只丢弃所有ICMP报文

    > ipflthookdrvtest.exe -f 0xFFFFFFFE
    OrigFirewallFlags   = 0xFFFFFFFF
    FirewallFlags       = 0xFFFFFFFE

    从本机ping别的机器

    > ping 192.168.7.254
    General failure.
    General failure.
    General failure.
    General failure.

    从别的机器ping本机

    > ping 192.168.7.152
    Request timed out.
    Request timed out.
    Request timed out.
    Request timed out.

    从本机ping本机

    > ping 127.0.0.1
    General failure.
    General failure.
    General failure.
    General failure.

只丢弃接收到的RST报文

    > ipflthookdrvtest.exe -f 0xFFFFFFFD
    OrigFirewallFlags   = 0xFFFFFFFE
    FirewallFlags       = 0xFFFFFFFD

    > telnet 192.168.7.100 1

    从本机telnet别的机器未监听的端口,按常理由于收到对端响应的RST报文而立
    即终止,但由于防火墙丢弃了这个RST报文,本机TCP/IP协议栈并不能得到RST报
    文,从而导致TCP有限状态机长时间保持在SYN_SENT状态,直至超时才销毁TCB。
    正常情况下很难在交互操作中看到SYN_SENT状态。

    > netstat -na | find /I "SYN_SENT"
    TCP    192.168.7.152:1038     192.168.7.100:1        SYN_SENT

可以用Sniffer配合验证过程。验证结束后卸载IpFilterHook:

> net stop IpFilterDriver
The following services are dependent on the IP Traffic Filter Driver service.
Stopping the IP Traffic Filter Driver service will also stop these services.

   IpFilterHook

Do you want to continue this operation? (Y/N) [N]: y

The IpFilterHook service was stopped successfully.
The IP Traffic Filter Driver service was stopped successfully.

> sc delete IpFilterHook
[SC] DeleteService SUCCESS

☆ 参考资源

[ 1] Network Devices and Protocols: Windows DDK
     Filter-Hook Driver Design Guide

     Network Devices and Protocols: Windows DDK
     Filter-Hook Driver Reference

[ 2] <<The Windows 2000 Device Driver Book, Second Edition>> - Art Baker, Jerry Lozano
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值