64位内核开发第十四讲,MiniFilter文件过滤框架以及安装方式

MiniFilter文件过滤第一讲 文件过滤框架以及安装方式

一丶MiniFilter 文件过滤框架

1.1 简介

MiniFilter是微软为我们开发的一个新的驱动,称为过滤管理器.(Filter Manager或者 fltmgr).这个驱动主要作用就是如果有文件操作可以通知我们.

MiniFilter的优点和不足如下:

优点:

1.增加开发速度

2.不用关心IRP处理工作,这些交给 Filter Manager处理即可.

不足:

MiniFilter开发的时候虽然简单了但是隐藏了很多细节.比如设备对象等等.如果使用以前的方式进行开发 那么就如同 C语言内嵌汇编 对兼容性不好 也失去了MiniFilter的意义.

1.2 MiniFilter框架

框架如下:

在IO管理器中我们的 MiniFilter会去进行注册. 如上图所示. 有 A B C三个.

而MiniFilter中最重要的是 高度值(Altitude) 不光有高度值还有分组.

比如A的分组就在 FSFilter Activity Monitor B在 FSFilter Anti-Virus 也就是反病毒层级. 高度越高越会被先执行.假设你拦截了文件访问你可以不发送给下一层. 这样 B C 就接受不到了. 所以这个高度值需要我们找微软申请.(但是不申请好像也能用.只要不影响即可)

高度值 是从 20000 ~ 429999 的.而高度值又有分组. 所以高度值不能乱写.一般就是每个分组有个高度值范围.

查询地址如下: 微筛选器驱动程序的加载顺序组和高度 - Windows drivers | Microsoft Docs

二丶MiniFilter 编程框架

2.1 简介

对应到程序来说 MiniFilter是很简单的. 只需要三个内核API就可以使用MiniFilter了.

而API中所需要的参数就是结构体. 所以我们搞清楚结构体中的参数就可以了. 其实就是往结构体里面填写东西即可.

内核API如下:

NTSTATUS
FltRegisterFilter(
IN PDRIVER_OBJECT Driver,                
IN CONST FLT_REGISTRATION *Registration,
OUT PFLT_FILTER *RetFilter
);
NTSTATUS
FltStartFiltering(
IN PFLT_FILTER Filter);
VOID
FltUnregisterFilter(
IN PFLT_FILTER Filter
);

API就三个. 分为 注册 启动 卸载 其中启动和卸载都是一个参数.就是Filter句柄.此句柄是从FltRegisterFilter 第三个参数传出的. 所以主要学习的就是第一个.

此函数有三个参数

  • 参数1 Driver 在DDK驱动中的 DriverEntry中的驱动对象.

  • 参数2 一个结构体 此结构体就是我们要了解的结构体.下面说.

  • 参数3 传出的句柄. 文件管理器的句柄. 注册成功后会传出句柄 给 启动 和卸载函数使用.

2.2 FLT_REGISTRATION 结构体

在我们的注册函数中有次结构体. 此结构体如下:

typedef struct _FLT_REGISTRATION {
USHORT Size; @1 指向自身的大小sizeof(FLT_REGISTRATION).
USHORT Version; 版本 必须设置为FLT_REGISTRATION_VERSION
FLT_REGISTRATION_FLAGS Flags; 标志 @1
CONST FLT_CONTEXT_REGISTRATION *ContextRegistration; 上下文@2
CONST FLT_OPERATION_REGISTRATION *OperationRegistration;
PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback;
PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback;
PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback;
PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback;
PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback;
PFLT_GENERATE_FILE_NAME GenerateFileNameCallback;
PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback;
PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback;
#if FLT_MGR_LONGHORN
PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback;
PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback;
#endif // FLT_MGR_LONGHORN
} FLT_REGISTRATION, *PFLT_REGISTRATION;

含义如下:

成员 含义 说明 是否重点 ⚪了解  √号 重点 ×极少或不使用
Size 大小 指向自身的大小 sizeof(FLT_REGISTRATION)
Version 版本 必须设置为 FLT_REGISTRATION_VERSION
Flags 标志 两种设置,设置为NULL或者 FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP 设置为STOP的时候 MinniFilter停止服务的时候不会进行卸载不管你的卸载函数是否设置
ContextRegistration 上下文 注册处理上下文的函数 如果注册了则结构体数组的最后一项必须设置为 FLT_CONTEXT_END
OperationRegistration 回调函数集 重点中的重点,主要学习的就是这个域怎么设置. 是一个结构体数组可以设置我们感兴趣的回调. 最后一项设置为 IRP_MJ_OPERATION_END
FilterUnloadCallback 卸载函数 卸载MiniFilter回调.如果flags = xx_STOP 那么不管你是否设置都不会卸载
InstanceSetupCallback 卷实例加载回调 当一个卷加载的时候MiniFilter会为其生成一个实例并且绑定,比如移动硬盘接入的时候就会生成一个实例. 可以设置为NULL.
InstanceQueryTeardownCallback 控制实例销毁函数 这个实例只会在手工解除绑定的时候会来.
InstanceTeardownStartCallback 实例销毁函数 当调用的时候代表已经解除绑定,可以设置为NULL
InstanceTeardownCompleteCallback 实例解绑定完成函数 当确定时调用解除绑定后的完成函数,可以设置为NULL.
GenerateFileNameCallback 文件名字回调 生成文件名可以设置回调,可以设置为NULL.
NormalizeNameComponentCallback 查询WDK ⚪×
NormalizeContextCleanupCallback 查询WDK ⚪×
TransactionNotificationCallback 查询WDK ⚪×
NormalizeNameComponentExCallback 查询WDK ⚪×

其实本质就是学习 回调函数集 他是一个对象数组.我们看下它的结构吧.

typedef struct _FLT_OPERATION_REGISTRATION {
UCHAR MajorFunction;
FLT_OPERATION_REGISTRATION_FLAGS Flags;
PFLT_PRE_OPERATION_CALLBACK PreOperation;
PFLT_POST_OPERATION_CALLBACK PostOperation;
PVOID Reserved1;
} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
  • 参数1 指明的你想监控的IRP操作

  • 参数2 是个标志

  • 参数3 是你执行的监控回调 pre代表的意思是先前回调. 比如文件创建 还未创建之前调用你

  • 参数4 监控后回调. 文件创建完会调用的回调

  • 参数五 保留参数 给NULL即可.

IRP可以监控很多 这个查询WDK文档即可.

这里说一下 标志

标志如下:

标志 含义
FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO 使用此标志代表了不对缓存的IO处理进行 pre和post函数操作 适用于快速IO 因为所有快速IO已经缓存
FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO 指定不应该为分页操作的IO进行回调操作.对于不是基于IRP的io操作都会跳过.不会调用我们的函数

看着比较蒙对吧. 那我说一下. 其实在我们写一个文件的时候并不是直接写入到磁盘中.

而是先写到缓存中的. 缓存在写到内存中的.

调用链大概如下:

APP->IO->FSD->Cache->MM->IO->FSD->DISk 第一种
APP->IO->FSD->DISk 第二种

第一种就是先写到缓存中,当满足1024个字节的时候再由MM发起IO请求.然后在通知文件系统最好写道磁盘中.

第二种就是直接通过IO到文件系统,然后写入到磁盘中.

如果频繁读写是影响效率的.所以对于第一种不是IRP发起的请求我们都可以忽略掉.

所以这两个标志的意思就是差不多这个意思.

2.3 pre回调 和post回调

pre回调函数原型如下:

typedef FLT_PREOP_CALLBACK_STATUS
(*PFLT_PRE_OPERATION_CALLBACK) (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext
);

post回调函数如下:

typedef FLT_POSTOP_CALLBACK_STATUS
(FLTAPI *PFLT_POST_OPERATION_CALLBACK) (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
);

2.3.1 pre返回值和post返回值

首先说一下返回值

pre返回值如下

返回值 含义 是否是重点
FLT_PREOP_SUCCESS_WITH_CALLBACK 完成回调的调用并且callbackData往下发,post中可以使用CallbackData
FLT_PREOP_SUCCESS_NO_CALLBACK 完成回调,不带参数往下发.
FLT_PREOP_PENDING 挂起
FLT_PREOP_DISALLOW_FASTIO 禁用Fastio
FLT_PREOP_COMPLETE 完成回调,不会往下发
FLT_PREOP_SYNCHRONIZE 同步

其实主要就是三个常用的就是 FLT_PREOP_SUCCESS_WITH_CALLBACKFLT_PREOP_COMPLETE

POST回调

返回值 含义 是否常用
FLT_POSTOP_FINISHED_PROCESSING 完成,筛选器管理器将继续完成 I/O 操作的处理。
FLT_POSTOP_MORE_PROCESSING_REQUIRED 微筛选器驱动程序已停止 I/O 操作的完成处理,但它不会将操作的控制权返回给筛选器管理器。
FLT_POSTOP_DISALLOW_FSFILTER_IO 微筛选器驱动程序不允许快速 QueryOpen 操作,并强制该操作沿慢速路径向下。这样做会导致 I/O 管理器通过执行文件的打开/查询/关闭来为请求提供服务。微筛选器驱动程序应仅返回 QueryOpen 的此状态。

2.3.2 PFLT_CALLBACK_DATA 数据获取

此参数是参数1 是很重要的参数. 其结构如下

typedef struct _FLT_CALLBACK_DATA {
FLT_CALLBACK_DATA_FLAGS Flags;
PETHREAD CONST Thread;
PFLT_IO_PARAMETER_BLOCK CONST Iopb;
IO_STATUS_BLOCK IoStatus;
struct _FLT_TAG_DATA_BUFFER *TagData;
union {
struct {
LIST_ENTRY QueueLinks;
PVOID QueueContext[2];
};
PVOID FilterContext[4];
};
KPROCESSOR_MODE RequestorMode;
} FLT_CALLBACK_DATA, *PFLT_CALLBACK_DATA;

其中记录了线程. 标志 iopb 以及 IoStatus

线程可以判断是否是哪个进程的

PFLT_CALLBACK_DATA Data;
PEPROCESS processObject =
Data->Thread ? IoThreadToProcess(Data->Thread) : PsGetCurrentProcess();

其中Iopb域则记录了我们之前从IRP堆栈中要获取的Buffer值.

Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess
PVOID pQueryBuffer =
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
ULONG uQueryBufferSize =
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length
PMDL pReadMdl = Data->Iopb->Parameters.Read. MdlAddress;
PVOID pReadBuffer = Data->Iopb->Parameters.Read. ReadBuffer;
ULONG uReadLength = Data->Iopb->Parameters.Read.Length;

IoStatus则记录了读写操作的长度值

还可以判断Data是什么操作. 也有宏.

FLT_IS_IRP_OPERATION
FLT_IS_FASTIO_OPERATION
FLT_IS_FS_FILTER_OPERATION
例子
if (FLT_IS_FASTIO_OPERATION(DATA))
{
status = STATUS_FLT_DISALLOW_FAST_IO;
Data->IoStatus.Status = status;
Data->IoStatus.information = 0;
return FLT_PREOP_DISALLOW_FASTIO;
}

2.3.3 对象的获取 FltObjects

它也是一个结构,记录了所有的你可以使用到的对象.

typedef struct _FLT_RELATED_OBJECTS {
USHORT CONST Size;
USHORT CONST TransactionContext;
PFLT_FILTER CONST Filter;
PFLT_VOLUME CONST Volume;
PFLT_INSTANCE CONST Instance;
PFILE_OBJECT CONST FileObject;
PKTRANSACTION CONST Transaction;
} FLT_RELATED_OBJECTS, *PFLT_RELATED_OBJECTS;
typedef CONST struct _FLT_RELATED_OBJECTS *PCFLT_RELATED_OBJECTS;

常用的就是如下:

FltObjects->Volume,
FltObjects->Instance,
FltObjects->FileObject,
FltObjects->FileObject->DeviceObject

2.3.4 例子学习

在WDK7600的Src目录下 有MiniFilter框架. 可以学习一下. 而如果使用VS高版本则需要自己选择模板进行生成.

例子目录: x:\WinDDK\7600.16385.1\src\filesys\miniFilter

  • nullFilter

    一个基本的框架,没有过滤的用处.可以看看怎么写的.

  • passThrough

    一个完整的框架 所以过滤已经设置,但是并不使用.以它学习是最好的.

  • scanner

一个拦截框架可以看看例子.

三丶MiniFilter的使用

MiniFilter需要进行安装方式有两种 inf安装方式和动态加载方式.

3.1 Inf安装方式 以passThrough 此例子的Inf讲解

inf了解即可.使用的时候拷贝一个inf即可.里面东西换成自己的就行

Inf如下:

;;;
;;; PassThrough
;;;
;;;
;;; Copyright (c) 1999 - 2001, Microsoft Corporation
;;;
[Version]
Signature = "$Windows NT$"
Class = "ActivityMonitor" ;指明了驱动的分组,必须指定.
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;GUID 每个分组都有固定的GUID
Provider = %Msft% ;变量值 从STRING节中可以看到驱动提供者的名称
DriverVer = 06/16/2007,1.0.0.1 ;版本号
CatalogFile = passthrough.cat ;inf对应的cat 文件 可以不需要
[DestinationDirs]
DefaultDestDir = 12 ;告诉我们驱动拷贝到哪里 13代表拷贝到%windir%
MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers
;;
;; Default install sections
;;
[DefaultInstall]
OptionDesc = %ServiceDescription%
CopyFiles = MiniFilter.DriverFiles
[DefaultInstall.Services]
AddService = %ServiceName%,,MiniFilter.Service
;;
;; Default uninstall sections
;;
[DefaultUninstall]
DelFiles = MiniFilter.DriverFiles
[DefaultUninstall.Services]
DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting
;
; Services Section
;
[MiniFilter.Service] ;服务的一些信息
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\
Dependencies = "FltMgr" ;服务的依赖
ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER
StartType = 3 ;SERVICE_DEMAND_START
ErrorControl = 1 ;SERVICE_ERROR_NORMAL
LoadOrderGroup = "FSFilter Activity Monitor" ;文件过滤分组
AddReg = MiniFilter.AddRegistry ;文件过滤注册表需要添加的高度值等信息
;
; Registry Modifications
;
[MiniFilter.AddRegistry]
HKR,,"DebugFlags",0x00010001 ,0x0
HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%
;
; Copy Files
;
[MiniFilter.DriverFiles]
%DriverName%.sys
[SourceDisksFiles]
passthrough.sys = 1,,
[SourceDisksNames]
1 = %DiskId1%,,,
;;
;; String Section
;;
[Strings]
Msft = "Microsoft Corporation"
ServiceDescription = "PassThrough Mini-Filter Driver"
ServiceName = "PassThrough"
DriverName = "PassThrough"
DiskId1 = "PassThrough Device Installation Disk"
;Instances specific information.
DefaultInstance = "PassThrough Instance"
Instance1.Name = "PassThrough Instance"
Instance1.Altitude = "370030"
Instance1.Flags = 0x0 ; Allow all attachments

其实INF文件本质就是跟我们正常的DDK安装服务的方式一样.只不过他是在注册表中多加两个一个注册表键和实例.

INF安装后首先会拷贝驱动到 C:\\windows\\System32\driver

然后往注册表下面注册信息.

如下:

所以 如果我们动态加载驱动的时候需要多建立两个键 分别是 instances xxxinstance 然后添加高度值.

在动态加载的时候 我们的依赖驱动要修改为 FltMgr 分组要写为 FSFilter Activity Monitor

伪代码可使用的部分例子
创建驱动服务如下:

//创建驱动所对应的服务
hService = CreateService( hServiceMgr,
lpszDriverName, // 驱动程序的在注册表中的名字
lpszDriverName, // 注册表驱动程序的DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_FILE_SYSTEM_DRIVER, // 表示加载的服务是文件系统驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的Start 值 01234 五个选项 0 由系统核心进行加载 1 io子系统加载 2自动启动 3 手动启动 4禁止启动
SERVICE_ERROR_IGNORE, // 注册表驱动程序的ErrorControl 值
szDriverImagePath, // 注册表驱动程序的ImagePath 值
"FSFilter Activity Monitor",// 注册表驱动程序的Group 值 如果是文件过滤驱动动态加载则需要指定这个分组
NULL,
"FltMgr", // 注册表驱动程序的DependOnService 值 文件过滤驱动需要依赖FltMgr 需要在这里执行
NULL,
NULL);

其它比如启动服务 停止服务都是一样的.和DDK一样的.
安装文件过滤驱动的时候还需要我们为其在注册表中创建两个键.并且还需要设置高度值.
代码如下:

strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
strcat(szTempStr,lpszDriverName);
strcat(szTempStr,"\\Instances");
if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
{
return FALSE;
}
// 注册表驱动程序的DefaultInstance 值
strcpy(szTempStr,lpszDriverName);
strcat(szTempStr," Instance");
if(RegSetValueEx(hKey,"DefaultInstance",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
{
return FALSE;
}
RegFlushKey(hKey);//刷新注册表
RegCloseKey(hKey);
//-------------------------------------------------------------------------------------------------------
// SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances\\DriverName Instance子健下的键值项
//-------------------------------------------------------------------------------------------------------
strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
strcat(szTempStr,lpszDriverName);
strcat(szTempStr,"\\Instances\\");
strcat(szTempStr,lpszDriverName);
strcat(szTempStr," Instance");
if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
{
return FALSE;
}
// 注册表驱动程序的Altitude 值
strcpy(szTempStr,lpszAltitude);
if(RegSetValueEx(hKey,"Altitude",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
{
return FALSE;
}
// 注册表驱动程序的Flags 值
dwData=0x0;
if(RegSetValueEx(hKey,"Flags",0,REG_DWORD,(CONST BYTE*)&dwData,sizeof(DWORD))!=ERROR_SUCCESS)
{
return FALSE;
}
RegFlushKey(hKey);//刷新注册表
RegCloseKey(hKey);

3.2 使用驱动

inf安装完 只不过是注册了一个项到注册表.拷贝了驱动到系统目录.

我们可以使用两种方式启动

net start PassThrough
fltmc load PassThrough

两种方式都可以.
inf也可以通过 函数 SetupCopyOEMInfA 来进行加载. 当然我们一般都是右键点击进行安装.
如果是动态加载的方式,那么我们就按照动态加载的方式启动即可.

3.3 内核下预先准备MiniFilter的注册

其实跟3.1一样,3.1是应用层创建的注册表项. 而如果想保持应用层不变的情况下,那么就需要内核驱动在自己被加载的时候,自己注册为MiniFilter
也就是自己操作注册表即可.
下面提供例子.

/Call BOOLEAN result = PrepMiniFilter(my_driver_reg_path,L"324213");
NTSTATUS PrepMiniFilter(IN PUNICODE_STRING reg_path, IN PWSTR altiude) {
BOOLEAN result = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PWSTR driver_name = NULL;
WCHAR key_path[MAX_PATH] = {0};
WCHAR default_instance_value_data[MAX_PATH] = {0};
if (reg_path == NULL) return result;
if (reg_path->Buffer == NULL) return result;
if (reg_path->Length <= 0) return result;
if (altiude == NULL) return result;
if (altiude[0] == L'\0') return result;
do {
driver_name = wcsrchr(reg_path->Buffer, L'\\');
if (!MmIsAddressValid(driver_name)) break;
RtlZeroMemory(key_path, MAX_PATH * sizeof(WCHAR));
// swprintf(key_path, L"%ws\\Instances", driver_name);
status = RtlStringCbPrintfW(key_path, sizeof(key_path), L"%ws\\Instances",
driver_name);
if (!NT_SUCCESS(status)) break;
status = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES, key_path);
if (!NT_SUCCESS(status)) break;
// swprintf(Data, L"%ws Instance", &driver_name[1]);
status = RtlStringCbPrintfW(default_instance_value_data,
sizeof(default_instance_value_data),
L"%ws Instance", &driver_name[1]);
if (!NT_SUCCESS(status)) break;
status = RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES, key_path, L"DefaultInstance", REG_SZ,
default_instance_value_data,
(ULONG)(wcslen(default_instance_value_data) * sizeof(WCHAR) + 2));
if (!NT_SUCCESS(status)) break;
RtlZeroMemory(key_path, MAX_PATH * sizeof(WCHAR));
// swprintf(key_path, L"%ws\\Instances%ws Instance", driver_name,
// driver_name);
status = RtlStringCbPrintfW(key_path, sizeof(key_path),
L"%ws\\Instances%ws Instance", driver_name,
driver_name);
if (!NT_SUCCESS(status)) break;
status = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES, key_path);
if (!NT_SUCCESS(status)) break;
status = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES, key_path);
if (!NT_SUCCESS(status)) break;
status = RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES, key_path, L"Altitude", REG_SZ, altiude,
(ULONG)(wcslen(altiude) * sizeof(WCHAR) + 2));
if (!NT_SUCCESS(status)) break;
ULONG dwData = 0;
status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES, key_path, L"Flags",
REG_DWORD, &dwData, 4);
if (!NT_SUCCESS(status)) break;
result = TRUE;
} while (FALSE);
return result;
}

使用的时候查看注释即可. 一般DriverEntry的第二个成员就是 reg_path, 第一个参数传入reg_path,第二个参数传入你想创建的 Altitude值即可.

posted @   iBinary  阅读(7183)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2019-01-16 python学习第八讲,python中的数据类型,列表,元祖,字典,之字典使用与介绍
2018-01-16 内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写
2018-01-16 内核知识第六讲,内核编写规范,以及获取GDT表
2018-01-16 VS2015配置内核WDK7600环境,32位下.
点击右上角即可分享
微信分享提示