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_CALLBACK
和 FLT_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
值即可.