NtQueryObject 在 win8 x86 x64 下获取不到指定句柄文件名的原因

本文详细记录了在Win8系统中使用NtQueryInformationFile列出所有句柄,通过NtQueryObject找到指定句柄文件名并进行文件拷贝的操作中遇到的问题,特别指出Win8系统下ObjectTypeNumber不再等于28,导致获取文件名失败。提供了解决方案和相关解释。

由于研发的需要,碰到了这样一个问题,用 NtQueryInformationFile 列出系统的所有句柄,然后通过 NtQueryObject 找到指定句柄的文件名,将文件拷贝出来。但是 在 win8 x86  x64 下却获取不到指定句柄文件名,在国外论坛 与 MSDN 中到处疯狂搜索都没找到原因。调试了 N 次,才发现在 win8 x86  x64 系统下 ObjectTypeNumber不再等于 28,  win7 x32 type:28  win8 x32 type:31   win8 x64 type:30.


记录下来,总有人需要!

前言 上一次发布过的程序:【首发】检测文件的占用,具有学习和商业价值(By超级用户),可以使用,仿电脑管家 正文 对于怎么枚举文件句柄 ,上一帖子对此有介绍,核心代码大概如下:如果 (ZwQueryObject (handle, #ObjectTypeInformation, unicode, 0, size) ≠ #STATUS_INVALID_HANDLE )' 只要不是无效的,为什么,详细看下面的注释 ' 参数 ' Handle ' 对象的一个句柄获取信息。 ' ObjectInformationClass ' 指定一个OBJECT_INFORMATION_CLASS返回值的类型决定了信息在ObjectInformation缓冲区。 ' ObjectInformation ' 一个指向caller-allocated缓冲接收请求的信息。 ' ObjectInformationLength ' 指定的大小,以字节为单位,ObjectInformation缓冲区。 ' ReturnLength ' 一个指向变量的指针,接收的大小,以字节为单位,请求的关键信息。如果NtQueryObject STATUS_SUCCESS返回,返回的变量包含的数据量。如果NtQueryObject返回STATUS_BUFFER_OVERFLOW或STATUS_BUFFER_TOO_SMALL,您可以使用变量的值来确定所需的缓冲区大小。 ' 返回值 ' NtQueryObject返回STATUS_SUCCESS或适当的错误状态。可能的错误状态码包括以下: ' 返回代码 描述 ' STATUS_ACCESS_DENIED ' 有足够的权限来执行该cha询。 ' STATUS_INVALID_HANDLE ' 提供对象句柄无效。 ' STATUS_INFO_LENGTH_MISMATCH ' 信息长度不足以容纳数据。 unicode = 取空白字节集 (size) ZwQueryObject (handle, #ObjectTypeInformation, unicode, size, 0)' 读取信息的unicode文本 RtlUnicodeStringToAnsiString (ansi, unicode, 真)' 编码转换 ' RtlUnicodeStringToAnsiString例程将给定Unicode字符串转换成一个ANSI字符串。 str = 指针到文本 (ansi.Buffer) ' RtlFreeAnsiString常规版本存储由RtlUnicodeStringToAnsiString分配。 ' 参数 ' AnsiString ' 指针ANSI字符串缓冲区由RtlUnicodeStringToAnsiString以前分配的。 RtlFreeAnsiString (ansi) str = “无法获取”' 无效的怎么获取…… 返回 (str) 这一次呢更新了一个RemoteCloseHandle ,大概的原理是什么呢? 同时也采用了一些比较骚的方法,这种方法的限制较多,但是对于32位进程就很有效果。 NtClose在MSDN的大概介绍 1. NtClose is a generic routine that operates on any type of object. 2. Closing an open object handle causes that handle to become invalid. The system also decrements the handle count for the object and checks whether the object can be deleted. The system does not actually delete the object until all of the object's handles are closed and no referenced pointers remain. 3. A driver must close every handle that it opens as soon as the handle is no longer required. Kernel handles, which are those that are opened by a system thread or by specifying the OBJ_KERNEL_HANDLE flag, can be closed only when the previous processor mo
<think> 我们需要判断一个句柄(handle)是否在某个枚举(enum)定义的范围内。在不同的编程语言中,枚举的实现和检查方式可能不同。这里以C语言为例,因为枚举在C中较为基础,且问题可能涉及底层操作。 在C语言中,枚举本质上是整型常量,编译器会将枚举成员替换为对应的整数值。因此,一个枚举类型定义了一组连续的或不连续的整数常量。要判断一个整数值(句柄可能用整数表示)是否属于某个枚举,我们通常需要考虑以下情况: 1. **枚举是连续的**:例如,`enum { A=0, B=1, C=2 }`,那么值0、1、2都在枚举内。 2. **枚举是非连续的**:例如,`enum { A=0, B=2, C=5 }`,那么值0、2、5在枚举内,但1、3、4则不在。 由于枚举在编译后就是整数,所以C语言本身没有提供直接的方法来检查一个整数是否为某个枚举的有效值。因此,我们需要手动实现检查。 ### 方法 #### 1. 定义检查函数 我们可以编写一个函数,检查给定整数是否在枚举定义的值集合中。例如: ```c typedef enum { HANDLE_TYPE_A = 0, HANDLE_TYPE_B = 1, HANDLE_TYPE_C = 2, HANDLE_TYPE_MAX // 作为边界标记 } handle_type_t; // 检查handle值是否在枚举范围内 int is_valid_handle(int handle) { return (handle >= HANDLE_TYPE_A && handle < HANDLE_TYPE_MAX); } ``` 这种方法适用于连续枚举,并且要求枚举的最后有一个最大值标记(通常通过添加一个额外的枚举成员)。 #### 2. 非连续枚举的检查 如果枚举值非连续,我们可以使用一个有效值数组,然后检查给定的值是否在数组中。 ```c typedef enum { HANDLE_TYPE_A = 0, HANDLE_TYPE_B = 2, HANDLE_TYPE_C = 5 } handle_type_t; static const int valid_handles[] = { HANDLE_TYPE_A, HANDLE_TYPE_B, HANDLE_TYPE_C }; int is_valid_handle(int handle) { for (int i = 0; i < sizeof(valid_handles)/sizeof(valid_handles[0]); i++) { if (valid_handles[i] == handle) { return 1; // 有效 } } return 0; // 无效 } ``` #### 3. 使用宏自动生成(高级技巧) 如果枚举值可能变动,我们可以考虑使用宏来简化维护。例如: ```c #define VALID_HANDLES \ H(HANDLE_TYPE_A) \ H(HANDLE_TYPE_B) \ H(HANDLE_TYPE_C) typedef enum { HANDLE_TYPE_A = 0, HANDLE_TYPE_B = 2, HANDLE_TYPE_C = 5 } handle_type_t; // 定义函数 int is_valid_handle(int handle) { // 使用宏展开有效值比较 #define H(x) if (handle == x) return 1; VALID_HANDLES #undef H return 0; } ``` ### 注意事项 - **枚举的底层类型**:在C++11及以上,可以使用枚举类(enum class)并指定底层类型,但同样需要自己实现检查。 - **句柄的含义**:句柄可能不仅仅是一个枚举值,也可能是一个指针或其他类型,需要根据上下文确定。如果句柄是指针,则不能直接用上述方法判断。 - **性能考虑**:如果枚举值很多且非连续,线性查找可能影响性能,可以考虑使用哈希表或二分查找(如果有效值有序)。 ### 其他语言 - **C#**:可以使用`Enum.IsDefined`方法。 - **Java**:可以通过`EnumSet`或遍历枚举常量。 - **Python**:使用`Enum`时,可以用`isinstance(value, EnumType)`或检查值是否在`list(EnumType)`中。 ### 总结 在C语言中,由于枚举在编译后就是整数,且没有内置的检查机制,我们需要根据枚举是否连续来设计检查函数。对于连续枚举,可以通过范围检查;对于非连续枚举,需要手动列出有效值或使用宏来辅助。 §§相关问题§§ 1. 在C语言中,如何确保新增枚举成员后,枚举值检查函数能自动更新? 2. 如果枚举值与系统错误码重叠,如何避免检查时产生冲突? 3. 在C++11的强类型枚举(enum class)中,判断一个整数是否属于枚举的方法有何不同? 4. 对于非连续的大型枚举(如超过100个值),如何优化检查函数的性能? 5. 在Windows API编程中,句柄(HANDLE)通常是指针类型,能否用类似方法检查其有效性?如果不能,通常采用什么方式? </think> ### 如何判断句柄是否在枚举范围内 判断句柄是否在枚举范围内需要根据编程语言和具体场景采用不同方法。以下是常见实现方式: #### 1. C/C++ 中的标准方法 ```c // 假设枚举定义 typedef enum { HANDLE_FILE = 0x1000, HANDLE_SOCKET = 0x2000, HANDLE_MEMORY = 0x3000, HANDLE_MAX // 作为边界标记 } HandleType; // 检查函数 bool IsValidHandle(HandleType handle) { // 检查是否在连续范围内 return (handle >= HANDLE_FILE && handle < HANDLE_MAX); } // 针对非连续枚举的位掩码检查 #define VALID_HANDLE_MASK 0xF000 bool IsHandleInEnum(uint32_t handle) { return ((handle & VALID_HANDLE_MASK) != 0); } ``` #### 2. C++11/17 更安全的检查 ```cpp #include <type_traits> #include <array> enum class SafeHandle : uint32_t { File = 0x1000, Socket = 0x2000, Memory = 0x3000 }; // 编译时检查(C++17) template<typename T> constexpr bool IsValidHandleType(T value) { static_assert(std::is_enum_v<T>, "Requires enum type"); switch(value) { case SafeHandle::File: case SafeHandle::Socket: case SafeHandle::Memory: return true; default: return false; } } // 运行时检查 bool ValidateHandle(uint32_t raw) { constexpr std::array validValues = { static_cast<uint32_t>(SafeHandle::File), static_cast<uint32_t>(SafeHandle::Socket), static_cast<uint32_t>(SafeHandle::Memory) }; return std::find(validValues.begin(), validValues.end(), raw) != validValues.end(); } ``` #### 3. C# 的专用方法 ```csharp // 定义枚举 public enum HandleType { File = 0x1000, Socket = 0x2000, Memory = 0x3000 } // 检查方法 bool IsValidHandle(HandleType handle) { return Enum.IsDefined(typeof(HandleType), handle); } // 包含位标志的检查 [Flags] public enum HandleFlags { Readable = 1, Writable = 2, Executable = 4 } bool IsValidFlagCombination(HandleFlags flags) { const HandleFlags allValid = HandleFlags.Readable | HandleFlags.Writable | HandleFlags.Executable; return (flags & ~allValid) == 0; } ``` #### 4. 系统级句柄的特殊检查(Windows API) ```c #include <windows.h> bool IsSystemHandleValid(HANDLE h) { if (h == INVALID_HANDLE_VALUE || h == NULL) return false; // 尝试获取对象信息(真实环境需错误处理) BYTE buffer[sizeof(OBJECT_BASIC_INFORMATION)] = {0}; ULONG retLength = 0; NTSTATUS status = NtQueryObject( h, ObjectBasicInformation, buffer, sizeof(buffer), &retLength ); return NT_SUCCESS(status); } ``` #### 关键注意事项 1. **边界处理**:始终定义明确的枚举边界值(如 `MAX_HANDLE`) 2. **类型安全**:C++优先使用 `enum class` 避免隐式转换 3. **位标志验证**:位运算枚举需验证组合有效性 4. **系统句柄特殊性**:系统句柄需使用API验证,不能仅靠值判断 5. **性能考量**:高频调用场景避免线性搜索,使用位掩码或跳表
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值