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_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
值即可.
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/15809124.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能.

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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位下.