最近做的项目中需要获取到进程打开的注册表项,这里分享一个使用系统ObQueryNameString函数的方法,有人说使用这个函数容易蓝屏,其实是因为参数使用错误了,查阅MSDN文档可以看到有关ObQueryNameString的信息,如下所示。
ObQueryNameString routine
The ObQueryNameString routine supplies the name, if there is one, of a given object to which the caller has a pointer.
NTSTATUS ObQueryNameString( _In_ PVOID Object, _Out_opt_ POBJECT_NAME_INFORMATION ObjectNameInfo, _In_ ULONG Length, _Out_ PULONG ReturnLength );
- Obeject是指向对象的指针
- ObjectNameInfo是一个用于存放查询名称的缓冲区,其结构如下:
typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name; } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
- Length描述了缓冲区的大小
- ReturnLength描述返回的字节数
从上面的描述中可以看到ObQueryNameString的参数POBJECT_NAME_INFORMATION在指定的情况下是用于返回查询数据。根据_OBJECT_NAME_INFORMATION的结构看出,其内部就是一个UNICODE_STRING的结构,所以有人就直接把一个UNICODE_STRING结构的指针直接转换成了_OBJECT_NAME_INFORMATION作为参数传入,结果可想而知,当程序运行到这个位置就无情的死掉了。
经过翻阅WRK的代码查阅到了ObQueryNameString的实现部分,也弄清楚了为什么不能直接传入UNICODE_STRING的指针。如下代码所示,是WRK中对于传入的_OBJECT_NAME_INFORMATION指针进行的处理。
//其中ObjectNameInfo 也就是传入的_OBJECT_NAME_INFORMATION 缓冲区指针 t = (PWCHAR)(ObjectNameInfo + 1); //.......此处省略中间的代码,此部分代码位于WRK \base\ntos\config的123行 //..... //这一步是将Name的buffer直接指向了t ObjectNameInfo->Name.Buffer = t
看到这里原因已经很清晰了,传入的缓冲区应当是一个连续的缓冲区,且头部是一个UNICODE_STRING类型,UNICODE_STRING的Buffer直接指向了缓冲区下面的部分,所以有很多人直接传入了一个UNICODE_STRING却错误的原因,下面给出一段通过注册表Object获取路径代码。
//必须要在DISPATCH_LEVEL 之下 if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { return FALSE; } //创建一个连续的缓冲区,UNICODE_STRING头部外加512个WCHAR字符 PUNICODE_STRING fileNameInfo = (PUNICODE_STRING)ExAllocatePool(PagedPool, 1024 + sizeof(UNICODE_STRING)); if (fileNameInfo == NULL) { return FALSE; } __try { ULONG uReturnLength = 0; if (!NT_SUCCESS(ObQueryNameString(pKeyObject, (POBJECT_NAME_INFORMATION)fileNameInfo, 1024 + sizeof(UNICODE_STRING), &uReturnLength))) { KdPrint(("错误\n")); ExFreePool(fileNameInfo); return FALSE; } else { //成功直接输入测试 KdPrint(("---->%08X,%S\n", (ULONG)pKeyObject, fileNameInfo->Buffer)); ExFreePool(fileNameInfo); } } __except (1) { KdPrint(("失败")); ExFreePool(fileNameInfo); return FALSE; } return TRUE;