写这篇文章不仅仅只为介绍功能的实现,也是为了作为一篇笔记来备忘,因此我会写的比较详细。文中的注释有的来自微软官方的解释翻译,有的来自Windows内核安全与驱动开发书中的解释,也有的来自我个人的理解。代码是在Windows内核安全与驱动开发第13章中minifilter代码的基础上完成的(我实现的功能源码中没有)。
吾爱破解那篇同名文章也是我写的,吾爱id:白影33
MyMinifilter.sys:实现根据应用层软件输入,选择是否文本内容替换,过滤readbuffer内容,禁止notepad打开。
MyDriverR3:系统驱动没有交互的界面,通过MyDriverR3与驱动进行交互。
默认情况下:安装驱动后,读取true.txt的内容会被替换为false.txt的内容。(false.txt里全为0,ture.txt全为1)
当在MyDriverR3输入52pojie:读取true.txt,就是ture.txt。
再次启动MyDriverR3输入0:读取true.txt的内容会被替换为false.txt的内容。
大家应该注意到了,要过滤文本内容,为什么要禁止打开notepad,原因是filemap方式(解决办法我发评论),我在实践中,使用debugview查看打印替换或的缓冲区内容,显示替换成false.txt,然而打开notepad却还是true.txt里的内容,我又在网上下了个EditPlus测试,用EditPlus打开查看为false.txt,替换成功,我又下了个notepad++测试,也是替换成功,所以有了这么一个折中的办法,禁止打开notepad,这个很简单对create进行过滤,是打开notepad就让它失败,(评论里我发了一个好点的办法,具体看评论)
这里说明我遇到的难点,以及解决办法。
难点:对目标文件打开后无IRP_MJ_READ的IRP。
解决方案:通过查阅书籍,发现为window文件缓冲机制导致的问题
window文件缓冲机制是:
window读文件会首先从文件缓冲中读取,如果文件缓冲中没有,
就会触发缺页异常从而发送IRP_MJ_READ从硬盘里读取,放入文件缓冲中。
如果文件缓冲中有,那就直接从内存中读取,不会发送IRP_MJ_READ。
对于这个问题书籍里用CcPurgeCacheSection然后和一些函数清除文件缓冲,
不知道是不是因为我的是minifilter而他的是普通的文件过滤驱动的区别,我调用该函数即使返回显示成功,依然没有IRP_MJ_READ,书上的办法对于我来说,行不通。
在一次测试中发现:如果对文件内容进行修改,点击保存就会有IRP_MJ_READ,
据此猜想,window在每次对原文件的修改保存后,会发送IRP_MJ_READ从硬盘上重新读取内容同步到文件缓冲里,这样我就有了一个大胆的想法。
在DriverEntry里用ZwWriteFile对文件写入空格(注意重入问题),这样就会触发read的irp,我就把false.txt内容替换到文件缓冲,然后在NPMiniMessage与应用层交互的函数里,也对文件进行写入触发read的irp,根据应用层输入的字符串,来判断替换为那个文件的内容
这里是记录自己在写驱动过程中遇到的问题,以及解决办法。
问题1:驱动安装蓝屏,windbg调试后发现FltRegisterFilter注册函数失败
解决办法:minifilter需要修改注册表,我用Windows内核安全与驱动开发的minifilter的inf文件右键安装后,再用drivermonitor安装FltRegisterFilter就返回成功
问题2:minifilter读后操作回调例程 里的PAGED_CODE();断下,PAGED_CODE();检查irql是否低于或等于APC_LEVEL,不成立就中断
解决办法:通过查阅微软官方文档发现minifilter后操作回调例程可以运行在高于APC_LEVEL的irql,不需要用PAGED_CODE来检查。
问题3:FltGetFileNameInformation会不时蓝屏
解决办法:通过对比官方代码,发现一些失败的情况没有直接返回,把而是失败的irp的数据给FltGetFileNameInformation会蓝屏,加上代码
if (!NT_SUCCESS( Data->IoStatus.Status ) || (STATUS_REPARSE == Data->IoStatus.Status))
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
检查,如果失败就直接返回,不调用FltGetFileNameInformation。
问题4:ZwReadFile/ZwWriteFile蓝屏
解决办法: 参数 8_Inopt PLARGE_INTEGER ByteOffset,不能直接给数,
例如:错误ZwReadFile(myhand2, NULL, NULL, NULL, &BLOCK2, str2, 500, 50, NULL);
正确 LARGE_INTEGER size2.QuadPart = 0;
status = ZwReadFile(myhand2, NULL, NULL, NULL, &BLOCK2, str2, 500, &size2, NULL);
(血的教训,蓝了我一下午)
问题5:RtlCopyMemory蓝屏
解决办法:我是在post回调函数中使用这个函数,post函数IRQL高一些,
微软官方的解释的意思是如果在高IRQL使用,原地址和目标地址的内存最好都是非分页内存。
问题6:DriverEntry的ZwReadFile蓝屏,
解决办法:调用ZwReadFile读取目标文件内容用于替换,而我又过滤了read的,read里面又需要用到这个ZwReadFile读取的内容
此时为空,导致蓝屏,解决办法用一个变量,读完后给它赋值,而后在read过滤时检查,如果变量没有赋值,就不过滤。
使用我发的文件里的inf安装驱动,然后用drivermonitor启动,过滤的文件位置默认为c盘,就是c:\,想放在其他位置修改源码里的路径就行,true.txt和false.txt复制到这个路径就行了,当然你也可以自己创建这两个文件,
过滤目标文件也可以通过修改源码里的名字进行修改,
编译环境:vs2013+wdk8.1,测试成功系统:win7x64
所有的文件链接:https://pan.baidu.com/s/1eyOwGI7XoCok1KN00_xPEg
提取码:ras6
解压密码:52pojie
NTSTATUS
DriverEntry(
__in PDRIVER_OBJECT DriverObject,
/
/
PDRIVER_OBJECT 驱动数据结构(驱动对象)的指针
__in PUNICODE_STRING RegistryPath
/
/
PUNICODE_STRING 内核字符串数组,驱动以服务的形式加载,这个字符串为驱动在注册表的路径
)
{
NTSTATUS status;
/
/
返回值状态
PSECURITY_DESCRIPTOR sd;
/
/
安全描述符。
OBJECT_ATTRIBUTES oa;
/
/
对象属性
UNICODE_STRING uniString;
/
/
用于通信端口名称
UNREFERENCED_PARAMETER(RegistryPath);
/
/
避免编译器关于未引用参数的警告
/
/
注册FilterRegistration,告诉系统我设置的回调例程
/
/
第
1
个参数是本驱动的驱动对象,是在入口函数DriverEntry 中作为参数传入的。
/
/
第
2
个参数就是一个注册信息的结构,这个结构内含描述这个过滤器的所有信息
/
/
第
3
个参数 是一个返回参数,返回注册成功的微过滤器句柄,在下面调用函数FltStartFiltering时会用到
/
/
DbgPrint(
"statuswww=\n"
);
status
=
FltRegisterFilter(DriverObject,
&FilterRegistration,
&gFilterHandle);
ASSERT(NT_SUCCESS(status));
/
/
检查结果是否成功,失败后触发异常被调试器接管
if
(NT_SUCCESS(status)) {
/
/
检查结果是否成功,
/
/
/
/
开始函数,只有一个参数为之前获取的句柄
/
/
status
=
FltStartFiltering(gFilterHandle);
if
(!NT_SUCCESS(status)) {
/
/
如果失败就取消注册,只有一个参数为之前获取的句柄
FltUnregisterFilter(gFilterHandle);
}
}
/
/
FltBuildDefaultSecurityDescriptor构建一个默认的安全描述符,用于FltCreateCommunicationPort。
/
/
参数
1
指向调用方分配的变量的指针,该变量接收到指向新创建的不透明指针
/
/
参数
2
指定调用者需要对端口对象的访问类型的标志的位掩码。系统定义的DesiredAccess标志集确定了minifilter驱动程序通信端口对象的以下特定访问权限。
status
=
FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
if
(!NT_SUCCESS(status)) {
/
/
检查结果是否成功,未成功就跳向最终处理部分
goto final;
}
/
/
函数初始化
Unicode
字符的计数字符串
RtlInitUnicodeString(&uniString, MINISPY_PORT_NAME);
/
/
宏初始化不透明的OBJECT_ATTRIBUTES结构,该结构将对象句柄的属性指定给打开句柄的例程。
/
/
参数
1
为要被初始化的结构体,参数
2
一个指向
Unicode
字符串的指针,该字符串包含要为其打开句柄的对象的名称。
/
/
这必须是一个完全限定的对象名,或者由RootDirectory参数指定的对象目录的相对路径名。
/
/
参数
3
标志位,此处指定为只在内核模式访问,与对大小写不区分
/
/
参数
4
在ObjectName参数中指定的路径名的根对象目录句柄。如果ObjectName是完全限定的对象名称,
/
/
则RootDirectory为空。使用ZwCreateDirectoryObject获得对象目录的句柄。
/
/
参数
5
指定创建对象时应用于该对象的安全描述符。此参数是可选的。驱动程序可以指定NULL来接受对象的默认安全性。
InitializeObjectAttributes(&oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd);
/
/
创建一个通信服务器端口,minifilter驱动程序可以在该端口上接收来自用户模式应用程序的连接请求。
/
/
参数
1
调用者的不透明过滤器指针
/
/
参数
2
指向调用方分配的变量的指针,该变量接收通信服务器端口的不透明端口句柄。minifilter驱动程序使用这个句柄来监听来自用户模式应用程序的连接请求。
/
/
参数
3
指向OBJECT_ATTRIBUTES结构的指针,该结构指定通信服务器端口的属性。
/
/
此结构必须由之前的InitializeObjectAttributes调用初始化。此参数是必需的,不能为空。通信端口对象的此结构的成员包括以下内容。
/
/
参数
4
指向由minifilter驱动程序定义的上下文信息的指针。这个信息可以用来区分由相同的minifilter驱动程序创建的多个通信服务器端口。
/
/
过滤器管理器将这个上下文指针作为参数传递给ConnectNotifyCallback例程。该参数是可选的,可以为NULL。
/
/
参数
5
NPMiniConneet是用户态与内核态建立连接时内核会调用到的函数。
/
/
参数
6
NPMiniDisconnect 是用户态与内核态连接结束时内核会调用到的函数。。
/
/
参数
7
NPMiniMessage是用户态与内核态传送数据时内核会调用到的函数
/
/
参数
8
此服务器端口所允许的最大并发客户端连接数。此参数是必需的,且必须大于零。
status
=
FltCreateCommunicationPort(gFilterHandle,
&gServerPort,
&oa,
NULL,
NPMiniConnect,
NPMiniDisconnect,
NPMiniMessage,
1
);
/
/
释放分配的安全描述符
FltFreeSecurityDescriptor(sd);
if
(!NT_SUCCESS(status)) {
goto final;
}
/
/
返回值
/
/
首先初始化含有文件路径的OBJECT_ATTRIBU
UNICODE_STRING mytext1;
/
/
文件名字符串
OBJECT_ATTRIBUTES ATTRIBUTES1;
IO_STATUS_BLOCK BLOCK1;
/
/
可拥有的控制权
LARGE_INTEGER size1;
/
/
写的偏移大小
str1
=
ExAllocatePool(NonPagedPool,
500
);
/
/
分配
500
的非分页内存
RtlZeroMemory(str1,
500
);
/
/
清
0
内存
RtlInitUnicodeString(&mytext1, L
"\\??\\C:\\true.txt"
);
/
/
初始化文件名
memset(&ATTRIBUTES1,
0
, sizeof(OBJECT_ATTRIBUTES));
/
/
对象属性清空
size1.QuadPart
=
0
;
InitializeObjectAttributes(&ATTRIBUTES1, &mytext1, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写 ,参数意义同上
/
/
打开目标文件获取句柄
/
/
参数
1
:一个指向句柄变量的指针,该句柄用于接收文件的句柄。
/
/
参数
2
:指定ACCESS_MASK值,该值确定请求对对象的访问。除了为所有类型的对象定义的访问权限之外,调用者还可以指定以下特定于文件的访问权限。
/
/
参数
3
:OBJECT_ATTRIBUTES结构的指针,该结构指定对象名和其他属性。使用InitializeObjectAttributes初始化该结构。
/
/
参数
4
:用于接收最终完成状态和请求操作的其他信息
/
/
参数
5
:它包含创建或覆盖的文件的初始分配大小,可以为Null
/
/
参数
6
:表示创建或覆盖文件时要设置的文件属性。调用者通常指定FILE_ATTRIBUTE_NORMAL,它设置默认属性
/
/
参数
7
:共享访问的类型,
/
/
参数
8
:指定在文件存在或不存在时执行的操作
/
/
参数
9
:指定驱动程序创建或打开文件时应用的选项
/
/
参数
10
:对于设备和中间驱动程序,此参数必须为空指针
/
/
参数
11
:对于设备和中间驱动程序,此参数必须为零。
status
=
ZwCreateFile(&myhandl, GENERIC_ALL, &ATTRIBUTES1, &BLOCK1, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
/
/
通过句柄读取目标文件放入str1
if
(!NT_SUCCESS(status))
{
FirsT
=
0
;
goto final;
}
else
{
status
=
ZwReadFile(myhandl, NULL, NULL, NULL, &BLOCK1, str1,
500
, &size1, NULL);
FirsT
=
1
;
/
/
防止重入
DbgPrint(
"status=%X\n"
, status);
}
/
/
以下内容基本同上只是文件的对象不一样
UNICODE_STRING mytext2;
OBJECT_ATTRIBUTES ATTRIBUTES2;
IO_STATUS_BLOCK BLOCK2;
/
/
可拥有的控制权
LARGE_INTEGER size2;
str2
=
ExAllocatePool(NonPagedPool,
500
);
RtlZeroMemory(str2,
500
);
RtlInitUnicodeString(&mytext2, L
"\\??\\C:\\false.txt"
);
/
/
初始化文件名
memset(&ATTRIBUTES2,
0
, sizeof(OBJECT_ATTRIBUTES));
/
/
对象属性清空
size2.QuadPart
=
0
;
InitializeObjectAttributes(&ATTRIBUTES2, &mytext2, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写
status
=
ZwCreateFile(&myhand2, GENERIC_ALL, &ATTRIBUTES2, &BLOCK2, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
if
(!NT_SUCCESS(status))
{
FirsT
=
0
;
goto final;
}
else
{
status
=
ZwReadFile(myhand2, NULL, NULL, NULL, &BLOCK2, str2,
500
, &size2, NULL);
DbgPrint(
"status2=%X\n"
, status);
}
/
/
对目标文件进行写入,从而触发read
PVOID str3
=
ExAllocatePool(PagedPool,
10
);
RtlZeroMemory(str3,
6
);
RtlCopyMemory(str3,
"..."
, strlen(
"..."
));
size1.HighPart
=
0
;
size1.QuadPart
=
500
;
status
=
ZwWriteFile(myhandl, NULL, NULL, NULL, &BLOCK1, str3,
6
, &size1, NULL);
/
/
关闭句柄
ZwClose(myhandl);
ZwClose(myhand2);
final :
/
/
失败处理处
if
(!NT_SUCCESS(status)) {
if
(NULL !
=
gServerPort) {
/
/
关闭过滤器驱动程序的通信服务器端口
FltCloseCommunicationPort(gServerPort);
}
if
(NULL !
=
gFilterHandle) {
/
/
取消注册
FltUnregisterFilter(gFilterHandle);
}
}
return
status;
}
FLT_PREOP_CALLBACK_STATUS
MyPostRead(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(CompletionContext);
char FileName[
260
]
=
"X:"
;
/
/
存放解析后的文件名字
NTSTATUS status;
/
/
返回值状态
PFLT_FILE_NAME_INFORMATION nameInfo;
/
/
名称信息
/
/
/
/
对失败的irp进行过滤,如果失败了,就不用继续
if
(!NT_SUCCESS(Data
-
>IoStatus.Status) ||
(STATUS_REPARSE
=
=
Data
-
>IoStatus.Status)) {
return
FLT_POSTOP_FINISHED_PROCESSING;
}
/
/
FltGetFileNameInformation例程返回文件或目录的名称信息。
status
=
FltGetFileNameInformation(Data,
/
/
PFLT_CALLBACK_DATA结构的指针,它是I
/
O操作的回调数据结构。此参数是必需的,不能为空。
FLT_FILE_NAME_NORMALIZED |
/
/
使nameInfo参数接收包含文件规范化名称的结构的地址。
FLT_FILE_NAME_QUERY_DEFAULT,
/
/
如果查询文件系统以获取文件名目前还不安全,则FltGetFileNameInformation将不执行任何操作。
/
/
否则,FltGetFileNameInformation查询过滤器管理器的名称缓存以获取文件名信息。
/
/
如果在缓存中没有找到该名称,则FltGetFileNameInformation查询文件系统并缓存结果。
&nameInfo);
/
/
包含文件名信息。
if
(NT_SUCCESS(status))
{
/
/
解析FLT_FILE_NAME_INFORMATION结构的内容。
status
=
FltParseFileNameInformation(nameInfo);
/
/
参数
1
大小写转换,后放入参数
2
if
(NPUnicodeStringToChar(&nameInfo
-
>Name, FileName))
{
/
/
DbgPrint(
"FileName is %s\n"
, FileName);
if
(strstr(FileName,
"TRUE.TXT"
) >
0
)
{
PUCHAR buf1;
if
(Data
-
>Iopb
-
>Parameters.Read.MdlAddress !
=
0
)
{
/
/
如果为mdl方式读取文件,就获取地址
buf1
=
MmGetSystemAddressForMdl(Data
-
>Iopb
-
>Parameters.Read.MdlAddress);
}
else
{
/
/
如果为ReadBuffer就直接给指针
buf1
=
Data
-
>Iopb
-
>Parameters.Read.ReadBuffer;
}
/
/
判断应用层发来的指令是否为显示true.txt
if
(command
=
=
2
)
{
if
(FirsT
=
=
1
)
{
/
/
复制内存,填写长度
RtlCopyMemory(buf1, str1,
500
);
Data
-
>IoStatus.Information
=
Data
-
>Iopb
-
>Parameters.Read.Length;
}
}
else
{
/
/
DbgBreakPoint();
if
(FirsT
=
=
1
)
{
/
/
复制内存,填写长度
RtlCopyMemory(buf1, str2,
500
);
Data
-
>IoStatus.Information
=
Data
-
>Iopb
-
>Parameters.Read.Length;
}
}
DbgPrint(
"ReadBuffed=%s"
, buf1);
/
/
释放文件名信息结构体的内存
FltReleaseFileNameInformation(nameInfo);
return
FLT_POSTOP_FINISHED_PROCESSING;
}
}
FltReleaseFileNameInformation(nameInfo);
}
return
FLT_POSTOP_FINISHED_PROCESSING;
}
NTSTATUS
NPMiniMessage(
__in PVOID ConnectionCookie,
__in_bcount_opt(InputBufferSize) PVOID InputBuffer,
__in ULONG InputBufferSize,
__out_bcount_part_opt(OutputBufferSize,
*
ReturnOutputBufferLength) PVOID OutputBuffer,
__in ULONG OutputBufferSize,
__out PULONG ReturnOutputBufferLength
)
{
/
/
写入文件需要的各种参数,意义同dirverEntry
NTSTATUS status;
UNICODE_STRING mytext1;
OBJECT_ATTRIBUTES ATTRIBUTES1;
IO_STATUS_BLOCK BLOCK1;
/
/
可拥有的控制权
LARGE_INTEGER size1;
PVOID str4
=
ExAllocatePool(PagedPool,
40
);
RtlZeroMemory(str4,
40
);
RtlInitUnicodeString(&mytext1, L
"\\??\\C:\\true.txt"
);
/
/
初始化文件名
memset(&ATTRIBUTES1,
0
, sizeof(OBJECT_ATTRIBUTES));
/
/
对象属性清空
size1.QuadPart
=
0
;
PAGED_CODE();
/
/
分配内存,存储字符串,用于和应用层字符串比较
PVOID test;
test
=
ExAllocatePool(PagedPool,
10
);
RtlCopyMemory(test,
"0"
, strlen(
"0"
));
PVOID test2;
test2
=
ExAllocatePool(PagedPool,
50
);
RtlCopyMemory(test2,
"52pojie"
, strlen(
"52pojie"
));
UNREFERENCED_PARAMETER(ConnectionCookie);
UNREFERENCED_PARAMETER(OutputBufferSize);
UNREFERENCED_PARAMETER(OutputBuffer);
/
/
通过应用层输入的字符串,来显示相应的文本
if
(strcmp(InputBuffer, test)
=
=
0
)
{
command
=
1
;
}
else
if
(strcmp(InputBuffer, test2)
=
=
0
)
{
command
=
2
;
}
else
{
status
=
STATUS_INVALID_PARAMETER;
}
/
/
向文件写入,来触发read,由于需要内容不同,所以通过改变ONOROFF,实现两种写入依次执行
if
(ONOROFF
=
=
0
)
{
/
/
向文件写入空格
ONOROFF
=
ONOROFF
+
1
;
InitializeObjectAttributes(&ATTRIBUTES1, &mytext1, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写
status
=
ZwCreateFile(&myhandl, GENERIC_ALL, &ATTRIBUTES1, &BLOCK1, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
RtlCopyMemory(str4,
" "
, strlen(
" "
));
size1.HighPart
=
0
;
size1.QuadPart
=
500
;
status
=
ZwWriteFile(myhandl, NULL, NULL, NULL, &BLOCK1, str4,
8
, &size1, NULL);
ZwClose(myhandl);
}
else
{
/
/
向文件写入
"****"
,
ONOROFF
=
ONOROFF
-
1
;
InitializeObjectAttributes(&ATTRIBUTES1, &mytext1, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写
status
=
ZwCreateFile(&myhandl, GENERIC_ALL, &ATTRIBUTES1, &BLOCK1, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
RtlCopyMemory(str4,
"****"
, strlen(
"****"
));
size1.HighPart
=
0
;
size1.QuadPart
=
500
;
status
=
ZwWriteFile(myhandl, NULL, NULL, NULL, &BLOCK1, str4,
8
, &size1, NULL);
ZwClose(myhandl);
}
/
/
释放内存
ExFreePool(str4);
ExFreePool(test);
ExFreePool(test2);
return
STATUS_SUCCESS;
}
BOOLEAN NPUnicodeStringToChar(PUNICODE_STRING UniName, char Name[])
{
ANSI_STRING AnsiName;
NTSTATUS ntstatus;
char
*
nameptr;
__try {
ntstatus
=
RtlUnicodeStringToAnsiString(&AnsiName, UniName, TRUE);
if
(AnsiName.Length <
260
) {
nameptr
=
(PCHAR)AnsiName.
Buffer
;
/
/
转换成大写和复制到缓冲区
strcpy(Name, _strupr(nameptr));
/
/
DbgPrint(
"NPUnicodeStringToChar : %s\n"
, Name);
}
RtlFreeAnsiString(&AnsiName);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DbgPrint(
"NPUnicodeStringToChar EXCEPTION_EXECUTE_HANDLER\n"
);
return
FALSE;
}
return
TRUE;
}
NTSTATUS NPInstanceSetup(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in FLT_INSTANCE_SETUP_FLAGS Flags,
__in DEVICE_TYPE VolumeDeviceType,
__in FLT_FILESYSTEM_TYPE VolumeFilesystemType
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType);
UNREFERENCED_PARAMETER(VolumeFilesystemType);
PAGED_CODE();
return
STATUS_SUCCESS;
}
NTSTATUS
NPInstanceQueryTeardown(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
PAGED_CODE();
return
STATUS_SUCCESS;
}
VOID NPInstanceTeardownStart(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in FLT_INSTANCE_TEARDOWN_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
PAGED_CODE();
}
VOID NPInstanceTeardownComplete(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in FLT_INSTANCE_TEARDOWN_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
PAGED_CODE();
}
NTSTATUS
NPUnload(
__in FLT_FILTER_UNLOAD_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(Flags);
PAGED_CODE();
ExFreePool(str1);
ExFreePool(str2);
FltCloseCommunicationPort(gServerPort);
FltUnregisterFilter(gFilterHandle);
return
STATUS_SUCCESS;
}
FLT_PREOP_CALLBACK_STATUS
NPPreCreate(
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID
*
CompletionContext
)
{
char FileName[
260
]
=
"X:"
;
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION nameInfo;
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
PAGED_CODE();
status
=
FltGetFileNameInformation(Data,
FLT_FILE_NAME_NORMALIZED |
FLT_FILE_NAME_QUERY_DEFAULT,
&nameInfo);
if
(NT_SUCCESS(status)) {
if
(
1
) {
FltParseFileNameInformation(nameInfo);
if
(NPUnicodeStringToChar(&nameInfo
-
>Name, FileName)) {
if
(strstr(FileName,
"NOTEPAD.EXE"
) >
0
) {
FltReleaseFileNameInformation(nameInfo);
return
FLT_PREOP_COMPLETE;
}
}
}
FltReleaseFileNameInformation(nameInfo);
}
return
FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
NTSTATUS
NPMiniConnect(
__in PFLT_PORT ClientPort,
__in PVOID ServerPortCookie,
__in_bcount(SizeOfContext) PVOID ConnectionContext,
__in ULONG SizeOfContext,
__deref_out_opt PVOID
*
ConnectionCookie
)
{
DbgPrint(
"[mini-filter] NPMiniConnect"
);
PAGED_CODE();
UNREFERENCED_PARAMETER(ServerPortCookie);
UNREFERENCED_PARAMETER(ConnectionContext);
UNREFERENCED_PARAMETER(SizeOfContext);
UNREFERENCED_PARAMETER(ConnectionCookie);
ASSERT(gClientPort
=
=
NULL);
gClientPort
=
ClientPort;
return
STATUS_SUCCESS;
}
VOID
NPMiniDisconnect(
__in_opt PVOID ConnectionCookie
)
{
PAGED_CODE();
UNREFERENCED_PARAMETER(ConnectionCookie);
DbgPrint(
"[mini-filter] NPMiniDisconnect"
);
/
/
Close our handle
FltCloseClientPort(gFilterHandle, &gClientPort);
}
NTSTATUS
DriverEntry(
__in PDRIVER_OBJECT DriverObject,
/
/
PDRIVER_OBJECT 驱动数据结构(驱动对象)的指针
__in PUNICODE_STRING RegistryPath
/
/
PUNICODE_STRING 内核字符串数组,驱动以服务的形式加载,这个字符串为驱动在注册表的路径
)
{
NTSTATUS status;
/
/
返回值状态
PSECURITY_DESCRIPTOR sd;
/
/
安全描述符。
OBJECT_ATTRIBUTES oa;
/
/
对象属性
UNICODE_STRING uniString;
/
/
用于通信端口名称
UNREFERENCED_PARAMETER(RegistryPath);
/
/
避免编译器关于未引用参数的警告
/
/
注册FilterRegistration,告诉系统我设置的回调例程
/
/
第
1
个参数是本驱动的驱动对象,是在入口函数DriverEntry 中作为参数传入的。
/
/
第
2
个参数就是一个注册信息的结构,这个结构内含描述这个过滤器的所有信息
/
/
第
3
个参数 是一个返回参数,返回注册成功的微过滤器句柄,在下面调用函数FltStartFiltering时会用到
/
/
DbgPrint(
"statuswww=\n"
);
status
=
FltRegisterFilter(DriverObject,
&FilterRegistration,
&gFilterHandle);
ASSERT(NT_SUCCESS(status));
/
/
检查结果是否成功,失败后触发异常被调试器接管
if
(NT_SUCCESS(status)) {
/
/
检查结果是否成功,
/
/
/
/
开始函数,只有一个参数为之前获取的句柄
/
/
status
=
FltStartFiltering(gFilterHandle);
if
(!NT_SUCCESS(status)) {
/
/
如果失败就取消注册,只有一个参数为之前获取的句柄
FltUnregisterFilter(gFilterHandle);
}
}
/
/
FltBuildDefaultSecurityDescriptor构建一个默认的安全描述符,用于FltCreateCommunicationPort。
/
/
参数
1
指向调用方分配的变量的指针,该变量接收到指向新创建的不透明指针
/
/
参数
2
指定调用者需要对端口对象的访问类型的标志的位掩码。系统定义的DesiredAccess标志集确定了minifilter驱动程序通信端口对象的以下特定访问权限。
status
=
FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
if
(!NT_SUCCESS(status)) {
/
/
检查结果是否成功,未成功就跳向最终处理部分
goto final;
}
/
/
函数初始化
Unicode
字符的计数字符串
RtlInitUnicodeString(&uniString, MINISPY_PORT_NAME);
/
/
宏初始化不透明的OBJECT_ATTRIBUTES结构,该结构将对象句柄的属性指定给打开句柄的例程。
/
/
参数
1
为要被初始化的结构体,参数
2
一个指向
Unicode
字符串的指针,该字符串包含要为其打开句柄的对象的名称。
/
/
这必须是一个完全限定的对象名,或者由RootDirectory参数指定的对象目录的相对路径名。
/
/
参数
3
标志位,此处指定为只在内核模式访问,与对大小写不区分
/
/
参数
4
在ObjectName参数中指定的路径名的根对象目录句柄。如果ObjectName是完全限定的对象名称,
/
/
则RootDirectory为空。使用ZwCreateDirectoryObject获得对象目录的句柄。
/
/
参数
5
指定创建对象时应用于该对象的安全描述符。此参数是可选的。驱动程序可以指定NULL来接受对象的默认安全性。
InitializeObjectAttributes(&oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd);
/
/
创建一个通信服务器端口,minifilter驱动程序可以在该端口上接收来自用户模式应用程序的连接请求。
/
/
参数
1
调用者的不透明过滤器指针
/
/
参数
2
指向调用方分配的变量的指针,该变量接收通信服务器端口的不透明端口句柄。minifilter驱动程序使用这个句柄来监听来自用户模式应用程序的连接请求。
/
/
参数
3
指向OBJECT_ATTRIBUTES结构的指针,该结构指定通信服务器端口的属性。
/
/
此结构必须由之前的InitializeObjectAttributes调用初始化。此参数是必需的,不能为空。通信端口对象的此结构的成员包括以下内容。
/
/
参数
4
指向由minifilter驱动程序定义的上下文信息的指针。这个信息可以用来区分由相同的minifilter驱动程序创建的多个通信服务器端口。
/
/
过滤器管理器将这个上下文指针作为参数传递给ConnectNotifyCallback例程。该参数是可选的,可以为NULL。
/
/
参数
5
NPMiniConneet是用户态与内核态建立连接时内核会调用到的函数。
/
/
参数
6
NPMiniDisconnect 是用户态与内核态连接结束时内核会调用到的函数。。
/
/
参数
7
NPMiniMessage是用户态与内核态传送数据时内核会调用到的函数
/
/
参数
8
此服务器端口所允许的最大并发客户端连接数。此参数是必需的,且必须大于零。
status
=
FltCreateCommunicationPort(gFilterHandle,
&gServerPort,
&oa,
NULL,
NPMiniConnect,
NPMiniDisconnect,
NPMiniMessage,
1
);
/
/
释放分配的安全描述符
FltFreeSecurityDescriptor(sd);
if
(!NT_SUCCESS(status)) {
goto final;
}
/
/
返回值
/
/
首先初始化含有文件路径的OBJECT_ATTRIBU
UNICODE_STRING mytext1;
/
/
文件名字符串
OBJECT_ATTRIBUTES ATTRIBUTES1;
IO_STATUS_BLOCK BLOCK1;
/
/
可拥有的控制权
LARGE_INTEGER size1;
/
/
写的偏移大小
str1
=
ExAllocatePool(NonPagedPool,
500
);
/
/
分配
500
的非分页内存
RtlZeroMemory(str1,
500
);
/
/
清
0
内存
RtlInitUnicodeString(&mytext1, L
"\\??\\C:\\true.txt"
);
/
/
初始化文件名
memset(&ATTRIBUTES1,
0
, sizeof(OBJECT_ATTRIBUTES));
/
/
对象属性清空
size1.QuadPart
=
0
;
InitializeObjectAttributes(&ATTRIBUTES1, &mytext1, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写 ,参数意义同上
/
/
打开目标文件获取句柄
/
/
参数
1
:一个指向句柄变量的指针,该句柄用于接收文件的句柄。
/
/
参数
2
:指定ACCESS_MASK值,该值确定请求对对象的访问。除了为所有类型的对象定义的访问权限之外,调用者还可以指定以下特定于文件的访问权限。
/
/
参数
3
:OBJECT_ATTRIBUTES结构的指针,该结构指定对象名和其他属性。使用InitializeObjectAttributes初始化该结构。
/
/
参数
4
:用于接收最终完成状态和请求操作的其他信息
/
/
参数
5
:它包含创建或覆盖的文件的初始分配大小,可以为Null
/
/
参数
6
:表示创建或覆盖文件时要设置的文件属性。调用者通常指定FILE_ATTRIBUTE_NORMAL,它设置默认属性
/
/
参数
7
:共享访问的类型,
/
/
参数
8
:指定在文件存在或不存在时执行的操作
/
/
参数
9
:指定驱动程序创建或打开文件时应用的选项
/
/
参数
10
:对于设备和中间驱动程序,此参数必须为空指针
/
/
参数
11
:对于设备和中间驱动程序,此参数必须为零。
status
=
ZwCreateFile(&myhandl, GENERIC_ALL, &ATTRIBUTES1, &BLOCK1, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
/
/
通过句柄读取目标文件放入str1
if
(!NT_SUCCESS(status))
{
FirsT
=
0
;
goto final;
}
else
{
status
=
ZwReadFile(myhandl, NULL, NULL, NULL, &BLOCK1, str1,
500
, &size1, NULL);
FirsT
=
1
;
/
/
防止重入
DbgPrint(
"status=%X\n"
, status);
}
/
/
以下内容基本同上只是文件的对象不一样
UNICODE_STRING mytext2;
OBJECT_ATTRIBUTES ATTRIBUTES2;
IO_STATUS_BLOCK BLOCK2;
/
/
可拥有的控制权
LARGE_INTEGER size2;
str2
=
ExAllocatePool(NonPagedPool,
500
);
RtlZeroMemory(str2,
500
);
RtlInitUnicodeString(&mytext2, L
"\\??\\C:\\false.txt"
);
/
/
初始化文件名
memset(&ATTRIBUTES2,
0
, sizeof(OBJECT_ATTRIBUTES));
/
/
对象属性清空
size2.QuadPart
=
0
;
InitializeObjectAttributes(&ATTRIBUTES2, &mytext2, OBJ_CASE_INSENSITIVE, NULL, NULL);
/
/
对象属性关键是文件名字 不区分大小写
status
=
ZwCreateFile(&myhand2, GENERIC_ALL, &ATTRIBUTES2, &BLOCK2, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE, NULL,
0
);
if
(!NT_SUCCESS(status))
{
FirsT
=
0
;
goto final;
}
else
{
status
=
ZwReadFile(myhand2, NULL, NULL, NULL, &BLOCK2, str2,
500
, &size2, NULL);
DbgPrint(
"status2=%X\n"
, status);
}
/
/
对目标文件进行写入,从而触发read
PVOID str3
=
ExAllocatePool(PagedPool,
10
);
RtlZeroMemory(str3,
6
);
RtlCopyMemory(str3,
"..."
, strlen(
"..."
));
size1.HighPart
=
0
;
size1.QuadPart
=
500
;
status
=
ZwWriteFile(myhandl, NULL, NULL, NULL, &BLOCK1, str3,
6
, &size1, NULL);
/
/
关闭句柄
ZwClose(myhandl);
ZwClose(myhand2);
final :
/
/
失败处理处
if
(!NT_SUCCESS(status)) {
if
(NULL !
=
gServerPort) {
/
/
关闭过滤器驱动程序的通信服务器端口
FltCloseCommunicationPort(gServerPort);
}
if
(NULL !
=
gFilterHandle) {
/
/
取消注册
FltUnregisterFilter(gFilterHandle);
}
}
return
status;
}
FLT_PREOP_CALLBACK_STATUS
MyPostRead(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(CompletionContext);
char FileName[
260
]
=
"X:"
;
/
/
存放解析后的文件名字
NTSTATUS status;
/
/
返回值状态
PFLT_FILE_NAME_INFORMATION nameInfo;
/
/
名称信息
/
/
/
/
对失败的irp进行过滤,如果失败了,就不用继续
if
(!NT_SUCCESS(Data
-
>IoStatus.Status) ||
(STATUS_REPARSE
=
=
Data
-
>IoStatus.Status)) {
return
FLT_POSTOP_FINISHED_PROCESSING;
}
/
/
FltGetFileNameInformation例程返回文件或目录的名称信息。
status
=
FltGetFileNameInformation(Data,
/
/
PFLT_CALLBACK_DATA结构的指针,它是I
/
O操作的回调数据结构。此参数是必需的,不能为空。
FLT_FILE_NAME_NORMALIZED |
/
/
使nameInfo参数接收包含文件规范化名称的结构的地址。
FLT_FILE_NAME_QUERY_DEFAULT,
/
/
如果查询文件系统以获取文件名目前还不安全,则FltGetFileNameInformation将不执行任何操作。
/
/
否则,FltGetFileNameInformation查询过滤器管理器的名称缓存以获取文件名信息。
/
/
如果在缓存中没有找到该名称,则FltGetFileNameInformation查询文件系统并缓存结果。
&nameInfo);
/
/
包含文件名信息。
if
(NT_SUCCESS(status))
{
/
/
解析FLT_FILE_NAME_INFORMATION结构的内容。
status
=
FltParseFileNameInformation(nameInfo);
/
/
参数
1
大小写转换,后放入参数
2
if
(NPUnicodeStringToChar(&nameInfo
-
>Name, FileName))
{
/
/
DbgPrint(
"FileName is %s\n"
, FileName);
if
(strstr(FileName,
"TRUE.TXT"
) >
0
)
{
PUCHAR buf1;
if
(Data
-
>Iopb
-
>Parameters.Read.MdlAddress !
=
0
)
{
/
/
如果为mdl方式读取文件,就获取地址
buf1
=
MmGetSystemAddressForMdl(Data
-
>Iopb
-
>Parameters.Read.MdlAddress);
}
else
{
/
/
如果为ReadBuffer就直接给指针
buf1
=
Data
-
>Iopb
-
>Parameters.Read.ReadBuffer;
}
/
/
判断应用层发来的指令是否为显示true.txt
if
(command
=
=
2
)
{
if
(FirsT
=
=
1
)
{
/
/
复制内存,填写长度
RtlCopyMemory(buf1, str1,
500
);
Data
-
>IoStatus.Information
=
Data
-
>Iopb
-
>Parameters.Read.Length;
}
}
else
{
/
/
DbgBreakPoint();
if
(FirsT
=
=
1
)
{
/
/
复制内存,填写长度
RtlCopyMemory(buf1, str2,
500
);
Data
-
>IoStatus.Information
=
Data
-
>Iopb
-
>Parameters.Read.Length;
}
}
DbgPrint(
"ReadBuffed=%s"
, buf1);
/
/
释放文件名信息结构体的内存
FltReleaseFileNameInformation(nameInfo);
return
FLT_POSTOP_FINISHED_PROCESSING;
}
}
FltReleaseFileNameInformation(nameInfo);
}
return
FLT_POSTOP_FINISHED_PROCESSING;
}
NTSTATUS
NPMiniMessage(
__in PVOID ConnectionCookie,
__in_bcount_opt(InputBufferSize) PVOID InputBuffer,
__in ULONG InputBufferSize,
__out_bcount_part_opt(OutputBufferSize,
*
ReturnOutputBufferLength) PVOID OutputBuffer,
__in ULONG OutputBufferSize,
__out PULONG ReturnOutputBufferLength
)
{
/
/
写入文件需要的各种参数,意义同dirverEntry
NTSTATUS status;
UNICODE_STRING mytext1;
OBJECT_ATTRIBUTES ATTRIBUTES1;
IO_STATUS_BLOCK BLOCK1;
/
/
可拥有的控制权
LARGE_INTEGER size1;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-12-28 17:22
被zx_838741编辑
,原因: