标题: MSDN系列(13)--"Filter-Hook Driver"入门 创建: 2004-07-14 19:35 更新: 2004-07-15 20:18 链接: https://scz.617.cn/windows/200407152022.txt -------------------------------------------------------------------------- ☆ 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可用。 用户态有一套Packet Filtering API,直接与IpFilterDriver打交道,做了适度优化, 可以根据srcIp、dstIp、srcPort、dstPort等等进行过滤,但与Filter-Hook Driver 无关!如果只关注IP地址和端口,可以考虑该套API,但试图过滤ICMP报文时就应考 虑Filter-Hook Driver。 注意,Filter-Hook Driver不是Firewall-Hook Driver。 从Windows XP开始,微软不推荐采用Firewall-Hook Driver实现防火墙。从网络层次 结构上看,Firewall-Hook Driver太高了。此外,Firewall-Hook Driver还将干挠到 ICS或其它个人防火墙。ICS即Internet Connection Sharing,可以简单理解成XP/ 2003自带的个人防火墙,当然事实上并非这么简单,ICS可以实现端口转发、NAT等类 似功能。如果你愿意,可以试着利用ICS将一台XP/2003配成NAT网关,我未实际测试, tk做过一些实验。对于XP/2003,推荐采用NDIS Intermediate 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 /* * typedef ULONG IPAddr, IPMask; */ #include #include /* * 用到了CTL_CODE宏 */ #include /************************************************************************ * * * 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 ); /* * 参看<>中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; /* * 参<>第三章 * 中"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 #include #include #include /************************************************************************ * * * 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,也可用\\。还应该尝试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 #include #include #include /* * 用到了CTL_CODE宏 */ #include /************************************************************************ * * * 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] <> - Art Baker, Jerry Lozano