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