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 @ 2022-01-16 12:09  iBinary  阅读(4350)  评论(0编辑  收藏  举报