📄 filemon学习笔记 --- windows文件过滤系统驱动开发 - 驱动开发 - 私のウェブサイト.htm
字号:
cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD width=650>
<DIV id=art style="MARGIN: 15px">
<DIV> WINDOWS文件过滤系统驱动开发,可用于硬盘还原,防病毒,文件安全防护,文件加密等诸多领域。而掌握核心层的理论及实践,对于成为一名优秀的开发人员不可或缺。</DIV>
<DIV> WINDOWS文件过滤系统驱动开发的两个经典例子,Filemon与SFilter,初学者在经过一定的理论积累后,对此两个例子代码的研究分析,会是步入驱动开发殿堂的重要一步,相信一定的理论积累以及贯穿剖析理解此两个例程后,就有能力开始进行文件过滤系统驱动开发的实际工作了。</DIV>
<DIV> 对于SFilter例子的讲解,楚狂人的教程已经比较流行,而Filemon例子也许因框架结构相对明晰,易于剖析理解,无人贴出教程,本人在剖析Filemon的过程中积累的一些笔记资料,陆续贴出希望对初学者有所帮助,并通过和大家的交流而互相提高。</DIV>
<DIV> Filemon学习笔记 第一篇:</DIV>
<DIV> Filemon的大致架构为,在此驱动程序中,创建了两类设备对象。</DIV>
<DIV> 一类设备对象用于和Filemon对应的exe程序通信,以接收用户输入信息,比如挂接或监控哪个分区,是否要挂接,是否要监控,监控何种操作等。此设备对象只创建了一个,在驱动程序的入口函数DriverEntry中。此类设备对象一般称为控制设备对象,并有名字,以方便应用层与其通信操作。</DIV>
<DIV> 第二类设备对象用于挂接到所须监控的分区,比如c:,d:或e:,f:,以便拦截到引应用层对该分区所执行的读,写等操作。此类设备对象为安全起见,一般不予命名,可根据须监控多少分区而创建一个或多个。</DIV>
<DIV> 驱动入口函数大致如下:</DIV>
<DIV>NTSTATUS<BR>DriverEntry(<BR> IN
PDRIVER_OBJECT DriverObject,<BR> IN
PUNICODE_STRING
RegistryPath<BR> )<BR>{<BR> NTSTATUS ntStatus;<BR> PDEVICE_OBJECT guiDevice;<BR> WCHAR
deviceNameBuffer[] =
L"DeviceFilemon";<BR> UNICODE_STRING deviceNameUnicodeString;<BR> WCHAR
deviceLinkBuffer[] =
L"DosDevicesFilemon";<BR> UNICODE_STRING deviceLinkUnicodeString;<BR> ULONG
i;<BR> DbgPrint (("Filemon.SYS: entering
DriverEntry<BR>"));<BR> FilemonDriver =
DriverObject;<BR> // <BR> // Setup the device
name<BR> // <BR> RtlInitUnicodeString
(&deviceNameUnicodeString,<BR> deviceNameBuffer
);<BR> //<BR> // Create the device used for GUI
communications<BR> //此设备对象用来和用户交互信息<BR> ntStatus
= IoCreateDevice (
DriverObject,<BR> sizeof(HOOK_EXTENSION),<BR> &deviceNameUnicodeString,<BR> FILE_DEVICE_FILEMON,<BR> 0,<BR> TRUE,<BR> &guiDevice
);<BR> //<BR> // If successful, make a symbolic
link that allows for the device<BR> // object's
access from Win32
programs<BR> //<BR> if(NT_SUCCESS(ntStatus))
{<BR> //<BR> // Mark this as our GUI
device<BR> //<BR> ((PHOOK_EXTENSION)
guiDevice->DeviceExtension)->Type =
GUIINTERFACE;<BR> //<BR> // Create a
symbolic link that the GUI can specify to gain
access<BR> // to this
driver/device<BR> //<BR> RtlInitUnicodeString
(&deviceLinkUnicodeString,<BR> deviceLinkBuffer
);<BR> ntStatus = IoCreateSymbolicLink
(&deviceLinkUnicodeString,<BR>
&deviceNameUnicodeString
);<BR> if(!NT_SUCCESS(ntStatus))
{<BR> DbgPrint (("Filemon.SYS:
IoCreateSymbolicLink
failed<BR>"));<BR> IoDeleteDevice( guiDevice
);<BR> return
ntStatus; <BR> }<BR> //<BR> //
Create dispatch points for all routines that must
be handled.<BR> // All entry points are
registered since we might filter a<BR> // file
system that processes all of
them.<BR> //<BR> for( i = 0; i <=
IRP_MJ_MAXIMUM_FUNCTION; i++ )
{<BR> DriverObject->MajorFunction[i] =
FilemonDispatch;<BR> }<BR>#if
DBG <BR> //<BR> // Driver unload is only
set if we are debugging Filemon. This is<BR> //
because unloading a filter is not really safe -
threads could<BR> // be in our fastio routines
(or about to enter them), for example,<BR> //
and there is no way to tell. When debugging, we
can risk the<BR> // occasional unload crash as
a trade-off for not having to<BR> // reboot as
often.<BR> //<BR> //
DriverObject->DriverUnload =
FilemonUnload;<BR>#endif //
DBG<BR> //<BR> // Set up the Fast I/O
dispatch
table<BR> //<BR> DriverObject->FastIoDispatch
= &FastIOHook;<BR> } else
{<BR> //<BR> // If something went wrong,
cleanup the device object and don't
load<BR> //<BR> DbgPrint(("Filemon: Failed
to create our device!<BR>"));<BR> return
ntStatus;<BR> }<BR> //<BR> // Initialize the
name hash table<BR> //<BR> for(i = 0; i <
NUMHASH; i++ ) HashTable[i] =
NULL;<BR> //<BR> // Find the process name
offset<BR> //<BR> ProcessNameOffset =
FilemonGetProcessNameOffset();//为了得到当前进程名字<BR> //<BR> //
Initialize the synchronization
objects<BR> //<BR>#if
DBG<BR> KeInitializeSpinLock( &CountMutex
);<BR>#endif<BR> ExInitializeFastMutex(
&LogMutex );<BR> ExInitializeResourceLite(
&FilterResource
);<BR> ExInitializeResourceLite(
&HashResource );<BR> //<BR> // Initialize a
lookaside for file
names<BR> //<BR> ExInitializeNPagedLookasideList(
&FullPathLookaside, NULL, NULL,<BR>
0, MAXPATHLEN, 'mliF', 256 );<BR> //<BR> //
Allocate the first output
buffer<BR> //<BR> CurrentLog = ExAllocatePool(
NonPagedPool, sizeof(*CurrentLog) );<BR> if(
!CurrentLog ) {<BR> //<BR> // Oops - we
can't do anything without at least one
buffer<BR> //<BR> IoDeleteSymbolicLink(
&deviceLinkUnicodeString
);<BR> IoDeleteDevice( guiDevice
);<BR> return
STATUS_INSUFFICIENT_RESOURCES;<BR> }<BR> //<BR> //
Set the buffer pointer to the start of the buffer
just allocated<BR> //<BR> CurrentLog->Len =
0;<BR> CurrentLog->Next = NULL;<BR> NumLog =
1;<BR> return
STATUS_SUCCESS;<BR>}在此驱动入口点函数中,主要做了生成新的设备对象,此设备对象用来和应用层信息交互,比如应用层向驱动传递需要挂接或者监控的分区盘符,或者是否挂接盘符,是否监控操作等。 上面创建设备对象的代码为:ntStatus
= IoCreateDevice (
DriverObject,<BR> sizeof(HOOK_EXTENSION),<BR> &deviceNameUnicodeString,<BR> FILE_DEVICE_FILEMON,<BR> 0,<BR> TRUE,<BR> &guiDevice
);<BR> //<BR> // If successful, make a symbolic
link that allows for the device<BR> // object's
access from Win32
programs<BR> //<BR> if(NT_SUCCESS(ntStatus))
{<BR> //<BR> // Mark this as our GUI
device<BR> //<BR> ((PHOOK_EXTENSION)
guiDevice->DeviceExtension)->Type =
GUIINTERFACE;<BR> //<BR> // Create a
symbolic link that the GUI can specify to gain
access<BR> // to this
driver/device<BR> //<BR> RtlInitUnicodeString
(&deviceLinkUnicodeString,<BR> deviceLinkBuffer
);<BR> ntStatus = IoCreateSymbolicLink
(&deviceLinkUnicodeString,<BR>
&deviceNameUnicodeString
);<BR> if(!NT_SUCCESS(ntStatus))
{<BR> DbgPrint (("Filemon.SYS:
IoCreateSymbolicLink
failed<BR>"));<BR> IoDeleteDevice( guiDevice
);<BR> return
ntStatus; <BR> }上面代码完成的功能为创建了用于与应用层交互的控制设备对象,名字在参数&deviceNameUnicodeString,中。设备对象创建成功后又调用IoCreateSymbolicLink创建了一个符号连接,以便于应用层交互。在入口点函数DriverEntry代码中,还有一处代码:
ProcessNameOffset =
FilemonGetProcessNameOffset();//为了得到当前进程名字。此函数体如下:ULONG<BR>FilemonGetProcessNameOffset(<BR> VOID<BR> )<BR>{<BR> PEPROCESS
curproc;<BR> int i;<BR> curproc =
PsGetCurrentProcess();//调用PsGetCurrentProcess取得KPEB基址<BR> //然后搜索KPEB,得到ProcessName相对KPEB的偏移量<BR> //
Scan for 12KB, hoping the KPEB never grows that
big!<BR> //<BR> for( i = 0; i < 3*PAGE_SIZE;
i++ ) {<BR> <BR> if( !strncmp( SYSNAME,
(PCHAR) curproc + i, strlen(SYSNAME) ))
{<BR> return
i;<BR> }<BR> }<BR> //<BR> // Name not found
- oh, well<BR> //<BR> return 0; 这个函数通过查找KPEB
(Kernel Process Environment
Block),取得进程名,GetProcessNameOffset主要是调用PsGetCurrentProcess取得KPEB基址,然后搜索
KPEB,得到ProcessName相对KPEB的偏移量,存放在全局变量ProcessNameOffset中,得到此偏移量的作用是:无论当前进程为哪个,其名字在KPEB中的偏移量不变,所以都可以通过此偏移量得到。而在入口点函数DriverEntry执行时,当前进程必为系统进程,所以在此函数中方便地根据系统进程名SYSNAME(#define
SYSNAME "System")得到此偏移量。</DIV>
<DIV> 分发函数剖析:</DIV>
<DIV> 在入口点函数中,通过代码:</DIV>
<DIV>for( i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION;
i++ ) {<BR> DriverObject->MajorFunction[i]
=
FilemonDispatch;<BR>}简单地把各个分发例程设置成了FilemonDispatch;
然后我们追踪其函数体:NTSTATUS<BR>FilemonDispatch(<BR> IN
PDEVICE_OBJECT DeviceObject,<BR> IN PIRP
Irp<BR> )<BR>{<BR> //<BR> // Determine if its a
request from the GUI to us, or one that is<BR> //
directed at a file system driver that we've
hooked<BR> //<BR> if( ((PHOOK_EXTENSION)
DeviceObject->DeviceExtension)->Type ==
GUIINTERFACE ) {<BR> return
FilemonDeviceRoutine( DeviceObject, Irp );<BR> }
else {<BR> return FilemonHookRoutine(
DeviceObject, Irp );<BR> }<BR>}</DIV>
<DIV> 函数体先判断需要处理IRP包的设备对象的类型,看是属于控制设备对象,还是属于用于挂接并监控文件读写操作的过滤设备对象。如果是属于后者
则进入:FilemonHookRoutine( DeviceObject, Irp
)此函数是拦截文件操作的中心,在其中获得了被操作的文件名字,并且根据操作类型,在</DIV>
<DIV> switch( currentIrpStack->MajorFunction )
{</DIV>
<DIV> }</DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -