ObQueryNameString on registry object

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the
callbacks (e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide
the registry path or only give partial path. I want to locate the full
registry path for these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is
to use ObQueryNameString. I implemented a method using this API, but so
far couldn’t get it to work properly. For RegNtDeleteKey,
RegNtDeleteValueKey, when I pass in the registry object pointer to
ObQueryNameString, I get an exception; for RegNtPostOpenKey, I get BSOD,
with PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is
the right way?

Thanks,

Hao

Hi Hao

I assume in delete path you call ObQueryNameString in pre-callback and in
open path post-callback you call only in case of success. If so then I’d
assume your call of ObQueryNameString is wrong somehow but I dont have the
telepathic facilities for further comment :slight_smile: Just a thought however - are
you thrown by the sentence “A pointer to a pointer to the registry key
object for the key to be deleted.” for example; because the words “a pointer
to” appear to have been repeated, which is incorrect. I do seem to recall
this came up on ntdev or ntfsd not all that long past?

Good Luck
Lyndon

“Hao Wang” wrote in message news:xxxxx@ntdev…

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the callbacks
(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the registry
path or only give partial path. I want to locate the full registry path for
these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is to
use ObQueryNameString. I implemented a method using this API, but so far
couldn’t get it to work properly. For RegNtDeleteKey, RegNtDeleteValueKey,
when I pass in the registry object pointer to ObQueryNameString, I get an
exception; for RegNtPostOpenKey, I get BSOD, with
PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is the
right way?

Thanks,
Hao

Hi Hao

I assume in delete path you call ObQueryNameString in pre-callback and in
open path post-callback you call only in case of success. If so then I’d
assume your call of ObQueryNameString is wrong somehow but I dont have the
telepathic facilities for further comment :slight_smile: Just a thought however - are
you thrown by the sentence “A pointer to a pointer to the registry key
object for the key to be deleted.” for example; because the words “a pointer
to” appear to have been repeated, which is incorrect. I do seem to recall
this came up on ntdev or ntfsd not all that long past?

Good Luck
Lyndon

“Hao Wang” wrote in message news:xxxxx@ntdev…

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the callbacks
(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the registry
path or only give partial path. I want to locate the full registry path for
these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is to
use ObQueryNameString. I implemented a method using this API, but so far
couldn’t get it to work properly. For RegNtDeleteKey, RegNtDeleteValueKey,
when I pass in the registry object pointer to ObQueryNameString, I get an
exception; for RegNtPostOpenKey, I get BSOD, with
PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is the
right way?

Thanks,
Hao

Hi, Lyndon,

On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
debugging sessions I have seen, this is called before a key is actually
deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
callback, which is called after the key is opened. So, the answer is yes
for both of your questions.

As for “a pointer to a pointer” documentation bug, yes, I am aware of
that (actually, I think you might be referring to another post I sent
not long ago on this subject. :slight_smile: In fact, I believe that in some cases
it is a pointer to a pointer, while in others it is only a pointer :slight_smile:

Attached bellow is the code segments related to this topic for further
discussion.

Thanks
Hao

/* This is the code for handling RegNtDeleteKey, called before a key is
deleted */
case RegNtDeleteKey:
{
PREG_DELETE_KEY_INFORMATION pInfo =
PREG_DELETE_KEY_INFORMATION) arg2;
if (pInfo != NULL){
if (pInfo->Object != NULL) {

//PUNICODE_STRING s =
QueryObjectFullPath(pInfo->Object);
// This one is actually a pointer
PUNICODE_STRING s = QueryObjectFullPath(
*((PVOID*) pInfo->Object));
if (s != NULL) {
DbgPrint(“RegNtDeleteKey %wZ”, s);
}
/* do other things with s */
}
}
break;
}

/* similar code for RegNtPostOpenKey */

PUNICODE_STRING QueryObjectFullPath(PVOID object)
{
PUNICODE_STRING path = NULL;
ULONG returnLength = 0;
char * buffer = NULL;
POBJECT_NAME_INFORMATION info;
NTSTATUS status;

if (KeGetCurrentIrql() < DISPATCH_LEVEL) {

// This is where the exception and PAGE_FAULT occurs
status = ObQueryNameString(object, (
POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );

if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
0)) {

buffer = (char *)
ExAllocatePoolWithTag(PagedPool,
returnLength * sizeof(WCHAR),
‘PAGE’);

info = (POBJECT_NAME_INFORMATION) buffer;

// This is where PAGE_FAULT occurs
if (ObQueryNameString(object,
info,
returnLength,
&returnLength) ==
STATUS_INFO_LENGTH_MISMATCH) {

return NULL;
}

path = (PUNICODE_STRING)
ExAllocatePoolWithTag(NonPagedPool, returnLength,
‘ipgD’);
RtlCopyUnicodeString(path, &(info->Name));

ExFreePoolWithTag(buffer, ‘PAGE’);
}
}

return path;
}

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
Sent: Wednesday, April 25, 2007 5:51 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] ObQueryNameString on registry object

Hi Hao

I assume in delete path you call ObQueryNameString in pre-callback and
in
open path post-callback you call only in case of success. If so then I’d

assume your call of ObQueryNameString is wrong somehow but I dont have
the
telepathic facilities for further comment :slight_smile: Just a thought however -
are
you thrown by the sentence “A pointer to a pointer to the registry key
object for the key to be deleted.” for example; because the words “a
pointer
to” appear to have been repeated, which is incorrect. I do seem to
recall
this came up on ntdev or ntfsd not all that long past?

Good Luck
Lyndon

“Hao Wang” wrote in message
news:xxxxx@ntdev…

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the
callbacks
(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
registry
path or only give partial path. I want to locate the full registry path
for
these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is
to
use ObQueryNameString. I implemented a method using this API, but so far

couldn’t get it to work properly. For RegNtDeleteKey,
RegNtDeleteValueKey,
when I pass in the registry object pointer to ObQueryNameString, I get
an
exception; for RegNtPostOpenKey, I get BSOD, with
PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is
the
right way?

Thanks,
Hao


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Although I have never used RegCallbacks, according to DDK’s doc, your
usage of ‘a pointer to a pointer’ is really funny.

In DDK:

typedef struct _REG_DELETE_KEY_INFORMATION {
PVOID Object;
} REG_DELETE_KEY_INFORMATION, *PREG_DELETE_KEY_INFORMATION;

Members
Object
Pointer to the registry key object for the key to be deleted.

It does not say Object is a pointer to a pointer to a Registry Object.
So I think the line you comment out is the correct one.

Other problems I can see in PreCreate are with ObQueryNameString.

It’s not necessary to multiple the buffer size with sizeof(WCHAR), eg.
buffer = (char *) ExAllocatePoolWithTag(PagedPool,
returnLength,
‘PAGE’);

That’s enough unless you wanna waste some more memory.

The second thing with ObQueryNameString is, you need to setup the
buffer before passing it to this API. Like this:

keyPath->Name.MaximumLength = Length - 2 * sizeof(ULONG);

Cheers,
R. Yang

  • Windows Kernel Developer [Custom Dev and Consulting]

On Wed, 25 Apr 2007 19:09:28 -0500, “Hao Wang”
wrote:

>
>Hi, Lyndon,
>
> On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
>debugging sessions I have seen, this is called before a key is actually
>deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
>callback, which is called after the key is opened. So, the answer is yes
>for both of your questions.
>
> As for “a pointer to a pointer” documentation bug, yes, I am aware of
>that (actually, I think you might be referring to another post I sent
>not long ago on this subject. :slight_smile: In fact, I believe that in some cases
>it is a pointer to a pointer, while in others it is only a pointer :slight_smile:
>
>
> Attached bellow is the code segments related to this topic for further
>discussion.
>
> Thanks
> Hao
>
>/* This is the code for handling RegNtDeleteKey, called before a key is
>deleted /
> case RegNtDeleteKey:
> {
> PREG_DELETE_KEY_INFORMATION pInfo =
>PREG_DELETE_KEY_INFORMATION) arg2;
> if (pInfo != NULL){
> if (pInfo->Object != NULL) {
>
> //PUNICODE_STRING s =
>QueryObjectFullPath(pInfo->Object);
> // This one is actually a pointer
> PUNICODE_STRING s = QueryObjectFullPath(
>
((PVOID*) pInfo->Object));
> if (s != NULL) {
> DbgPrint(“RegNtDeleteKey %wZ”, s);
> }
> /* do other things with s /
> }
> }
> break;
> }
>
>/
similar code for RegNtPostOpenKey */
>
>
>PUNICODE_STRING QueryObjectFullPath(PVOID object)
>{
> PUNICODE_STRING path = NULL;
> ULONG returnLength = 0;
> char * buffer = NULL;
> POBJECT_NAME_INFORMATION info;
> NTSTATUS status;
>
> if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
>
> // This is where the exception and PAGE_FAULT occurs
> status = ObQueryNameString(object, (
> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>
> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>0)) {
>
> buffer = (char *)
> ExAllocatePoolWithTag(PagedPool,
> returnLength * sizeof(WCHAR),
>‘PAGE’);
>
> info = (POBJECT_NAME_INFORMATION) buffer;
>
> // This is where PAGE_FAULT occurs
> if (ObQueryNameString(object,
> info,
> returnLength,
> &returnLength) ==
>STATUS_INFO_LENGTH_MISMATCH) {
>
> return NULL;
> }
>
> path = (PUNICODE_STRING)
> ExAllocatePoolWithTag(NonPagedPool, returnLength,
>‘ipgD’);
> RtlCopyUnicodeString(path, &(info->Name));
>
> ExFreePoolWithTag(buffer, ‘PAGE’);
> }
> }
>
> return path;
>}
>
>
>-----Original Message-----
>From: xxxxx@lists.osr.com
>[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
>Sent: Wednesday, April 25, 2007 5:51 PM
>To: Windows System Software Devs Interest List
>Subject: Re:[ntdev] ObQueryNameString on registry object
>
>Hi Hao
>
>I assume in delete path you call ObQueryNameString in pre-callback and
>in
>open path post-callback you call only in case of success. If so then I’d
>
>assume your call of ObQueryNameString is wrong somehow but I dont have
>the
>telepathic facilities for further comment :slight_smile: Just a thought however -
>are
>you thrown by the sentence “A pointer to a pointer to the registry key
>object for the key to be deleted.” for example; because the words “a
>pointer
>to” appear to have been repeated, which is incorrect. I do seem to
>recall
>this came up on ntdev or ntfsd not all that long past?
>
>Good Luck
>Lyndon
>
>“Hao Wang” wrote in message
>news:xxxxx@ntdev…
>
>Hi,
>
>I am working on an XP-based registry call back filter driver, using the
>RegistryNotifyCallback routine. I noticed that quite a few of the
>callbacks
>(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
>registry
>path or only give partial path. I want to locate the full registry path
>for
>these callbacks.
>
>After some searching on OSR, the only suggested solution, from 2004, is
>to
>use ObQueryNameString. I implemented a method using this API, but so far
>
>couldn’t get it to work properly. For RegNtDeleteKey,
>RegNtDeleteValueKey,
>when I pass in the registry object pointer to ObQueryNameString, I get
>an
>exception; for RegNtPostOpenKey, I get BSOD, with
>PAGE_FAULT_IN_NONPAGED_AREA as the fault code.
>
>
>I have two questions. First, is this API the correct way to obtain the
>information I need? Second, if so, what did I do wrong? If not, what is
>the
>right way?
>
>
>Thanks,
>Hao
>
>
>
>—
>Questions? First check the Kernel Driver FAQ at
>http://www.osronline.com/article.cfm?id=256
>
>To unsubscribe, visit the List Server section of OSR Online at
>http://www.osronline.com/page.cfm?name=ListServer

I think you should not expect ObQueryNameString will return ‘required Length’ when called with POBJECT_NAME_INFORMATION as NULL.
Just allocate, in first place, a POBJECT_NAME_INFORMATION structure with a ‘starting size’(say 1024 bytes) and increase it in case you get the STATUS_INFO_LENGTH_MISMATCH error. Do not try calling with NULL in first place.

-----Mensaje original-----
De: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]En nombre de Hao Wang
Enviado el: jueves, 26 de abril de 2007 2:09
Para: Windows System Software Devs Interest List
Asunto: RE: [ntdev] ObQueryNameString on registry object

Hi, Lyndon,

On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
debugging sessions I have seen, this is called before a key is actually
deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
callback, which is called after the key is opened. So, the answer is yes
for both of your questions.

As for “a pointer to a pointer” documentation bug, yes, I am aware of
that (actually, I think you might be referring to another post I sent
not long ago on this subject. :slight_smile: In fact, I believe that in some cases
it is a pointer to a pointer, while in others it is only a pointer :slight_smile:

Attached bellow is the code segments related to this topic for further
discussion.

Thanks
Hao

/* This is the code for handling RegNtDeleteKey, called before a key is
deleted */
case RegNtDeleteKey:
{
PREG_DELETE_KEY_INFORMATION pInfo =
PREG_DELETE_KEY_INFORMATION) arg2;
if (pInfo != NULL){
if (pInfo->Object != NULL) {

//PUNICODE_STRING s =
QueryObjectFullPath(pInfo->Object);
// This one is actually a pointer
PUNICODE_STRING s = QueryObjectFullPath(
*((PVOID*) pInfo->Object));
if (s != NULL) {
DbgPrint(“RegNtDeleteKey %wZ”, s);
}
/* do other things with s */
}
}
break;
}

/* similar code for RegNtPostOpenKey */

PUNICODE_STRING QueryObjectFullPath(PVOID object)
{
PUNICODE_STRING path = NULL;
ULONG returnLength = 0;
char * buffer = NULL;
POBJECT_NAME_INFORMATION info;
NTSTATUS status;

if (KeGetCurrentIrql() < DISPATCH_LEVEL) {

// This is where the exception and PAGE_FAULT occurs
status = ObQueryNameString(object, (
POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );

if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
0)) {

buffer = (char *)
ExAllocatePoolWithTag(PagedPool,
returnLength * sizeof(WCHAR),
‘PAGE’);

info = (POBJECT_NAME_INFORMATION) buffer;

// This is where PAGE_FAULT occurs
if (ObQueryNameString(object,
info,
returnLength,
&returnLength) ==
STATUS_INFO_LENGTH_MISMATCH) {

return NULL;
}

path = (PUNICODE_STRING)
ExAllocatePoolWithTag(NonPagedPool, returnLength,
‘ipgD’);
RtlCopyUnicodeString(path, &(info->Name));

ExFreePoolWithTag(buffer, ‘PAGE’);
}
}

return path;
}

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
Sent: Wednesday, April 25, 2007 5:51 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] ObQueryNameString on registry object

Hi Hao

I assume in delete path you call ObQueryNameString in pre-callback and
in
open path post-callback you call only in case of success. If so then I’d

assume your call of ObQueryNameString is wrong somehow but I dont have
the
telepathic facilities for further comment :slight_smile: Just a thought however -
are
you thrown by the sentence “A pointer to a pointer to the registry key
object for the key to be deleted.” for example; because the words “a
pointer
to” appear to have been repeated, which is incorrect. I do seem to
recall
this came up on ntdev or ntfsd not all that long past?

Good Luck
Lyndon

“Hao Wang” wrote in message
news:xxxxx@ntdev…

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the
callbacks
(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
registry
path or only give partial path. I want to locate the full registry path
for
these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is
to
use ObQueryNameString. I implemented a method using this API, but so far

couldn’t get it to work properly. For RegNtDeleteKey,
RegNtDeleteValueKey,
when I pass in the registry object pointer to ObQueryNameString, I get
an
exception; for RegNtPostOpenKey, I get BSOD, with
PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is
the
right way?

Thanks,
Hao


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Hi,

I believe that we are looking at two different documents :slight_smile: This is what
I am using: http://msdn2.microsoft.com/en-us/library/aa491676.aspx
and I have double checked to make sure that the object is in fact a
pointer to a pointer to a registry key object.

You are right about the unnecessary waste of memory. I am going to
change that.

A update on the problem. I believe that I might have tracked down the
issue. It has occurred to me that kernel stack size is rather small. So
if I have an object that is too big (say, 2K) sitting on the stack, will
this be an issue?

Thanks,
Hao

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of R. Yang
Sent: Wednesday, April 25, 2007 8:35 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] ObQueryNameString on registry object

Although I have never used RegCallbacks, according to DDK’s doc, your
usage of ‘a pointer to a pointer’ is really funny.

In DDK:

typedef struct _REG_DELETE_KEY_INFORMATION {
PVOID Object;
} REG_DELETE_KEY_INFORMATION, *PREG_DELETE_KEY_INFORMATION;

Members
Object
Pointer to the registry key object for the key to be deleted.

It does not say Object is a pointer to a pointer to a Registry Object.
So I think the line you comment out is the correct one.

Other problems I can see in PreCreate are with ObQueryNameString.

It’s not necessary to multiple the buffer size with sizeof(WCHAR), eg.
buffer = (char *) ExAllocatePoolWithTag(PagedPool,
returnLength,
‘PAGE’);

That’s enough unless you wanna waste some more memory.

The second thing with ObQueryNameString is, you need to setup the
buffer before passing it to this API. Like this:

keyPath->Name.MaximumLength = Length - 2 * sizeof(ULONG);

Cheers,
R. Yang

  • Windows Kernel Developer [Custom Dev and Consulting]

On Wed, 25 Apr 2007 19:09:28 -0500, “Hao Wang”
wrote:

>
>Hi, Lyndon,
>
> On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
>debugging sessions I have seen, this is called before a key is actually
>deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
>callback, which is called after the key is opened. So, the answer is
yes
>for both of your questions.
>
> As for “a pointer to a pointer” documentation bug, yes, I am aware of
>that (actually, I think you might be referring to another post I sent
>not long ago on this subject. :slight_smile: In fact, I believe that in some cases
>it is a pointer to a pointer, while in others it is only a pointer :slight_smile:
>
>
> Attached bellow is the code segments related to this topic for
further
>discussion.
>
> Thanks
> Hao
>
>/* This is the code for handling RegNtDeleteKey, called before a key is
>deleted /
> case RegNtDeleteKey:
> {
> PREG_DELETE_KEY_INFORMATION pInfo =
>PREG_DELETE_KEY_INFORMATION) arg2;
> if (pInfo != NULL){
> if (pInfo->Object != NULL) {
>
> //PUNICODE_STRING s =
>QueryObjectFullPath(pInfo->Object);
> // This one is actually a pointer
> PUNICODE_STRING s = QueryObjectFullPath(
>
((PVOID*) pInfo->Object));
> if (s != NULL) {
> DbgPrint(“RegNtDeleteKey %wZ”, s);
> }
> /* do other things with s /
> }
> }
> break;
> }
>
>/
similar code for RegNtPostOpenKey */
>
>
>PUNICODE_STRING QueryObjectFullPath(PVOID object)
>{
> PUNICODE_STRING path = NULL;
> ULONG returnLength = 0;
> char * buffer = NULL;
> POBJECT_NAME_INFORMATION info;
> NTSTATUS status;
>
> if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
>
> // This is where the exception and PAGE_FAULT occurs
> status = ObQueryNameString(object, (
> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>
> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>0)) {
>
> buffer = (char *)
> ExAllocatePoolWithTag(PagedPool,
> returnLength * sizeof(WCHAR),
>‘PAGE’);
>
> info = (POBJECT_NAME_INFORMATION) buffer;
>
> // This is where PAGE_FAULT occurs
> if (ObQueryNameString(object,
> info,
> returnLength,
> &returnLength) ==
>STATUS_INFO_LENGTH_MISMATCH) {
>
> return NULL;
> }
>
> path = (PUNICODE_STRING)
> ExAllocatePoolWithTag(NonPagedPool, returnLength,
>‘ipgD’);
> RtlCopyUnicodeString(path, &(info->Name));
>
> ExFreePoolWithTag(buffer, ‘PAGE’);
> }
> }
>
> return path;
>}
>
>
>-----Original Message-----
>From: xxxxx@lists.osr.com
>[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
>Sent: Wednesday, April 25, 2007 5:51 PM
>To: Windows System Software Devs Interest List
>Subject: Re:[ntdev] ObQueryNameString on registry object
>
>Hi Hao
>
>I assume in delete path you call ObQueryNameString in pre-callback and
>in
>open path post-callback you call only in case of success. If so then
I’d
>
>assume your call of ObQueryNameString is wrong somehow but I dont have
>the
>telepathic facilities for further comment :slight_smile: Just a thought however -
>are
>you thrown by the sentence “A pointer to a pointer to the registry key
>object for the key to be deleted.” for example; because the words “a
>pointer
>to” appear to have been repeated, which is incorrect. I do seem to
>recall
>this came up on ntdev or ntfsd not all that long past?
>
>Good Luck
>Lyndon
>
>“Hao Wang” wrote in message
>news:xxxxx@ntdev…
>
>Hi,
>
>I am working on an XP-based registry call back filter driver, using the

>RegistryNotifyCallback routine. I noticed that quite a few of the
>callbacks
>(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
>registry
>path or only give partial path. I want to locate the full registry path
>for
>these callbacks.
>
>After some searching on OSR, the only suggested solution, from 2004, is
>to
>use ObQueryNameString. I implemented a method using this API, but so
far
>
>couldn’t get it to work properly. For RegNtDeleteKey,
>RegNtDeleteValueKey,
>when I pass in the registry object pointer to ObQueryNameString, I get
>an
>exception; for RegNtPostOpenKey, I get BSOD, with
>PAGE_FAULT_IN_NONPAGED_AREA as the fault code.
>
>
>I have two questions. First, is this API the correct way to obtain the
>information I need? Second, if so, what did I do wrong? If not, what is
>the
>right way?
>
>
>Thanks,
>Hao
>
>
>
>—
>Questions? First check the Kernel Driver FAQ at
>http://www.osronline.com/article.cfm?id=256
>
>To unsubscribe, visit the List Server section of OSR Online at
>http://www.osronline.com/page.cfm?name=ListServer


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Hi Hao

I wonder are you douple-plus-sure this is pointer to pointer to object? It
would help me if you could perhaps paste a fragment of windbag session which
proves the point? I ask because, well, I’ll have to recheck my code :slight_smile:

By the way, 2Kb stack, yes that isnt a GoodThing™ at all for kernel code.
It might explain your bugcheck!

I’ll try find time to look over your code snippet tomorrow.

Best Wishes
Lyndon

“Hao Wang” wrote in message news:xxxxx@ntdev…

Hi,

I believe that we are looking at two different documents :slight_smile: This is what
I am using: http://msdn2.microsoft.com/en-us/library/aa491676.aspx
and I have double checked to make sure that the object is in fact a
pointer to a pointer to a registry key object.

You are right about the unnecessary waste of memory. I am going to
change that.

A update on the problem. I believe that I might have tracked down the
issue. It has occurred to me that kernel stack size is rather small. So
if I have an object that is too big (say, 2K) sitting on the stack, will
this be an issue?

Thanks,
Hao

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of R. Yang
Sent: Wednesday, April 25, 2007 8:35 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] ObQueryNameString on registry object

Although I have never used RegCallbacks, according to DDK’s doc, your
usage of ‘a pointer to a pointer’ is really funny.

In DDK:

typedef struct _REG_DELETE_KEY_INFORMATION {
PVOID Object;
} REG_DELETE_KEY_INFORMATION, PREG_DELETE_KEY_INFORMATION;

Members
Object
Pointer to the registry key object for the key to be deleted.

It does not say Object is a pointer to a pointer to a Registry Object.
So I think the line you comment out is the correct one.

Other problems I can see in PreCreate are with ObQueryNameString.

It’s not necessary to multiple the buffer size with sizeof(WCHAR), eg.
buffer = (char ) ExAllocatePoolWithTag(PagedPool,
returnLength,
‘PAGE’);

That’s enough unless you wanna waste some more memory.

The second thing with ObQueryNameString is, you need to setup the
buffer before passing it to this API. Like this:

keyPath->Name.MaximumLength = Length - 2 * sizeof(ULONG);

Cheers,
R. Yang
- Windows Kernel Developer [Custom Dev and Consulting]

On Wed, 25 Apr 2007 19:09:28 -0500, “Hao Wang”
wrote:

>
>Hi, Lyndon,
>
> On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
>debugging sessions I have seen, this is called before a key is actually
>deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
>callback, which is called after the key is opened. So, the answer is
yes
>for both of your questions.
>
> As for “a pointer to a pointer” documentation bug, yes, I am aware of
>that (actually, I think you might be referring to another post I sent
>not long ago on this subject. :slight_smile: In fact, I believe that in some cases
>it is a pointer to a pointer, while in others it is only a pointer :slight_smile:
>
>
> Attached bellow is the code segments related to this topic for
further
>discussion.
>
> Thanks
> Hao
>
>/
This is the code for handling RegNtDeleteKey, called before a key is
>deleted /
> case RegNtDeleteKey:
> {
> PREG_DELETE_KEY_INFORMATION pInfo =
>PREG_DELETE_KEY_INFORMATION) arg2;
> if (pInfo != NULL){
> if (pInfo->Object != NULL) {
>
> //PUNICODE_STRING s =
>QueryObjectFullPath(pInfo->Object);
> // This one is actually a pointer
> PUNICODE_STRING s = QueryObjectFullPath(
>
((PVOID
) pInfo->Object));
> if (s != NULL) {
> DbgPrint(“RegNtDeleteKey %wZ”, s);
> }
> /* do other things with s /
> }
> }
> break;
> }
>
>/
similar code for RegNtPostOpenKey */
>
>
>PUNICODE_STRING QueryObjectFullPath(PVOID object)
>{
> PUNICODE_STRING path = NULL;
> ULONG returnLength = 0;
> char * buffer = NULL;
> POBJECT_NAME_INFORMATION info;
> NTSTATUS status;
>
> if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
>
> // This is where the exception and PAGE_FAULT occurs
> status = ObQueryNameString(object, (
> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>
> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>0)) {
>
> buffer = (char *)
> ExAllocatePoolWithTag(PagedPool,
> returnLength * sizeof(WCHAR),
>‘PAGE’);
>
> info = (POBJECT_NAME_INFORMATION) buffer;
>
> // This is where PAGE_FAULT occurs
> if (ObQueryNameString(object,
> info,
> returnLength,
> &returnLength) ==
>STATUS_INFO_LENGTH_MISMATCH) {
>
> return NULL;
> }
>
> path = (PUNICODE_STRING)
> ExAllocatePoolWithTag(NonPagedPool, returnLength,
>‘ipgD’);
> RtlCopyUnicodeString(path, &(info->Name));
>
> ExFreePoolWithTag(buffer, ‘PAGE’);
> }
> }
>
> return path;
>}
>
>
>-----Original Message-----
>From: xxxxx@lists.osr.com
>[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
>Sent: Wednesday, April 25, 2007 5:51 PM
>To: Windows System Software Devs Interest List
>Subject: Re:[ntdev] ObQueryNameString on registry object
>
>Hi Hao
>
>I assume in delete path you call ObQueryNameString in pre-callback and
>in
>open path post-callback you call only in case of success. If so then
I’d
>
>assume your call of ObQueryNameString is wrong somehow but I dont have
>the
>telepathic facilities for further comment :slight_smile: Just a thought however -
>are
>you thrown by the sentence “A pointer to a pointer to the registry key
>object for the key to be deleted.” for example; because the words “a
>pointer
>to” appear to have been repeated, which is incorrect. I do seem to
>recall
>this came up on ntdev or ntfsd not all that long past?
>
>Good Luck
>Lyndon
>
>“Hao Wang” wrote in message
>news:xxxxx@ntdev…
>
>Hi,
>
>I am working on an XP-based registry call back filter driver, using the

>RegistryNotifyCallback routine. I noticed that quite a few of the
>callbacks
>(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
>registry
>path or only give partial path. I want to locate the full registry path
>for
>these callbacks.
>
>After some searching on OSR, the only suggested solution, from 2004, is
>to
>use ObQueryNameString. I implemented a method using this API, but so
far
>
>couldn’t get it to work properly. For RegNtDeleteKey,
>RegNtDeleteValueKey,
>when I pass in the registry object pointer to ObQueryNameString, I get
>an
>exception; for RegNtPostOpenKey, I get BSOD, with
>PAGE_FAULT_IN_NONPAGED_AREA as the fault code.
>
>
>I have two questions. First, is this API the correct way to obtain the
>information I need? Second, if so, what did I do wrong? If not, what is
>the
>right way?
>
>
>Thanks,
>Hao
>
>
>
>—
>Questions? First check the Kernel Driver FAQ at
>http://www.osronline.com/article.cfm?id=256
>
>To unsubscribe, visit the List Server section of OSR Online at
>http://www.osronline.com/page.cfm?name=ListServer


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

>

I believe that we are looking at two different documents :slight_smile: This is what
I am using: http://msdn2.microsoft.com/en-us/library/aa491676.aspx
and I have double checked to make sure that the object is in fact a
pointer to a pointer to a registry key object.

I copy it from 2K3 IFS Kit’s doc. Yours is from WDK’s doc. It seems
that one of them is wrong otherwise binaries can not be compatible on
Vista and Pre-Vista. Hope someone from MS can clarify this for us. Or
you can simply try any one of these statements.

You are right about the unnecessary waste of memory. I am going to
change that.

A update on the problem. I believe that I might have tracked down the
issue. It has occurred to me that kernel stack size is rather small. So
if I have an object that is too big (say, 2K) sitting on the stack, will
this be an issue?

The default stack size is 1K. Prefast can easily figure out such error
while compiling.

Cheers,
R. Yang

  • Windows Kernel Developer [Custom Dev and Consulting]

I would say that any function in a driver that uses more than 256 bytes is
excessive. I would try to keep it much less than that. It is true that
processing requests to a single software driver that doesn’t call down a
stack can use a lot more stack, but it is a bad habit. You never know who
might attach to your driver and how much stack they may consume either
directly or via calling back in at the top of the stack.

“Hao Wang” wrote in message news:xxxxx@ntdev…

Hi,

I believe that we are looking at two different documents :slight_smile: This is what
I am using: http://msdn2.microsoft.com/en-us/library/aa491676.aspx
and I have double checked to make sure that the object is in fact a
pointer to a pointer to a registry key object.

You are right about the unnecessary waste of memory. I am going to
change that.

A update on the problem. I believe that I might have tracked down the
issue. It has occurred to me that kernel stack size is rather small. So
if I have an object that is too big (say, 2K) sitting on the stack, will
this be an issue?

Thanks,
Hao

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of R. Yang
Sent: Wednesday, April 25, 2007 8:35 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] ObQueryNameString on registry object

Although I have never used RegCallbacks, according to DDK’s doc, your
usage of ‘a pointer to a pointer’ is really funny.

In DDK:

typedef struct _REG_DELETE_KEY_INFORMATION {
PVOID Object;
} REG_DELETE_KEY_INFORMATION, PREG_DELETE_KEY_INFORMATION;

Members
Object
Pointer to the registry key object for the key to be deleted.

It does not say Object is a pointer to a pointer to a Registry Object.
So I think the line you comment out is the correct one.

Other problems I can see in PreCreate are with ObQueryNameString.

It’s not necessary to multiple the buffer size with sizeof(WCHAR), eg.
buffer = (char ) ExAllocatePoolWithTag(PagedPool,
returnLength,
‘PAGE’);

That’s enough unless you wanna waste some more memory.

The second thing with ObQueryNameString is, you need to setup the
buffer before passing it to this API. Like this:

keyPath->Name.MaximumLength = Length - 2 * sizeof(ULONG);

Cheers,
R. Yang
- Windows Kernel Developer [Custom Dev and Consulting]

On Wed, 25 Apr 2007 19:09:28 -0500, “Hao Wang”
wrote:

>
>Hi, Lyndon,
>
> On XP, I believe RegNtPreDeleteKey = RegNtDeleteKey, and based on the
>debugging sessions I have seen, this is called before a key is actually
>deleted. For OpenKey, yes, I am dealing with the RegNtPostOpenKey
>callback, which is called after the key is opened. So, the answer is
yes
>for both of your questions.
>
> As for “a pointer to a pointer” documentation bug, yes, I am aware of
>that (actually, I think you might be referring to another post I sent
>not long ago on this subject. :slight_smile: In fact, I believe that in some cases
>it is a pointer to a pointer, while in others it is only a pointer :slight_smile:
>
>
> Attached bellow is the code segments related to this topic for
further
>discussion.
>
> Thanks
> Hao
>
>/
This is the code for handling RegNtDeleteKey, called before a key is
>deleted /
> case RegNtDeleteKey:
> {
> PREG_DELETE_KEY_INFORMATION pInfo =
>PREG_DELETE_KEY_INFORMATION) arg2;
> if (pInfo != NULL){
> if (pInfo->Object != NULL) {
>
> //PUNICODE_STRING s =
>QueryObjectFullPath(pInfo->Object);
> // This one is actually a pointer
> PUNICODE_STRING s = QueryObjectFullPath(
>
((PVOID
) pInfo->Object));
> if (s != NULL) {
> DbgPrint(“RegNtDeleteKey %wZ”, s);
> }
> /* do other things with s /
> }
> }
> break;
> }
>
>/
similar code for RegNtPostOpenKey */
>
>
>PUNICODE_STRING QueryObjectFullPath(PVOID object)
>{
> PUNICODE_STRING path = NULL;
> ULONG returnLength = 0;
> char * buffer = NULL;
> POBJECT_NAME_INFORMATION info;
> NTSTATUS status;
>
> if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
>
> // This is where the exception and PAGE_FAULT occurs
> status = ObQueryNameString(object, (
> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>
> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>0)) {
>
> buffer = (char *)
> ExAllocatePoolWithTag(PagedPool,
> returnLength * sizeof(WCHAR),
>‘PAGE’);
>
> info = (POBJECT_NAME_INFORMATION) buffer;
>
> // This is where PAGE_FAULT occurs
> if (ObQueryNameString(object,
> info,
> returnLength,
> &returnLength) ==
>STATUS_INFO_LENGTH_MISMATCH) {
>
> return NULL;
> }
>
> path = (PUNICODE_STRING)
> ExAllocatePoolWithTag(NonPagedPool, returnLength,
>‘ipgD’);
> RtlCopyUnicodeString(path, &(info->Name));
>
> ExFreePoolWithTag(buffer, ‘PAGE’);
> }
> }
>
> return path;
>}
>
>
>-----Original Message-----
>From: xxxxx@lists.osr.com
>[mailto:xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
>Sent: Wednesday, April 25, 2007 5:51 PM
>To: Windows System Software Devs Interest List
>Subject: Re:[ntdev] ObQueryNameString on registry object
>
>Hi Hao
>
>I assume in delete path you call ObQueryNameString in pre-callback and
>in
>open path post-callback you call only in case of success. If so then
I’d
>
>assume your call of ObQueryNameString is wrong somehow but I dont have
>the
>telepathic facilities for further comment :slight_smile: Just a thought however -
>are
>you thrown by the sentence “A pointer to a pointer to the registry key
>object for the key to be deleted.” for example; because the words “a
>pointer
>to” appear to have been repeated, which is incorrect. I do seem to
>recall
>this came up on ntdev or ntfsd not all that long past?
>
>Good Luck
>Lyndon
>
>“Hao Wang” wrote in message
>news:xxxxx@ntdev…
>
>Hi,
>
>I am working on an XP-based registry call back filter driver, using the

>RegistryNotifyCallback routine. I noticed that quite a few of the
>callbacks
>(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the
>registry
>path or only give partial path. I want to locate the full registry path
>for
>these callbacks.
>
>After some searching on OSR, the only suggested solution, from 2004, is
>to
>use ObQueryNameString. I implemented a method using this API, but so
far
>
>couldn’t get it to work properly. For RegNtDeleteKey,
>RegNtDeleteValueKey,
>when I pass in the registry object pointer to ObQueryNameString, I get
>an
>exception; for RegNtPostOpenKey, I get BSOD, with
>PAGE_FAULT_IN_NONPAGED_AREA as the fault code.
>
>
>I have two questions. First, is this API the correct way to obtain the
>information I need? Second, if so, what did I do wrong? If not, what is
>the
>right way?
>
>
>Thanks,
>Hao
>
>
>
>—
>Questions? First check the Kernel Driver FAQ at
>http://www.osronline.com/article.cfm?id=256
>
>To unsubscribe, visit the List Server section of OSR Online at
>http://www.osronline.com/page.cfm?name=ListServer


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

// This is where the exception and PAGE_FAULT occurs
status = ObQueryNameString(object, (
POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );

You pass NULL pointer as POBJECT_NAME_INFORMATION, i.e. pointer to a structure that into which data is supposed to be copied. What else, apart from the exception, would you expect here???
It does not matter that you request 0 bytes to be returned - a pointer still has to be valid.

Here is one more “masterpiece”:

if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
0)) {

buffer = (char *)
ExAllocatePoolWithTag(PagedPool,
returnLength * sizeof(WCHAR),
‘PAGE’);

info = (POBJECT_NAME_INFORMATION) buffer;

// This is where PAGE_FAULT occurs
if (ObQueryNameString(object,
info,
returnLength,
&returnLength) ==
STATUS_INFO_LENGTH_MISMATCH) {

return NULL;
}

Look at the declaration:

typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

Therefore, you have to do something like

OBJECT_NAME_INFORMATION info;

info.Name. Buffer=ExAllocatePool(PagedPool, size);
info.Name. Length=info.Name. MaximumLength=size;

At this point you can pass ‘info’ to ObQueryNameString().

Instead of doing things this way, you just allocate a buffer, and pass it to ObQueryNameString().
How is ObQueryNameString() supposed to interpretate it???

In other words, your code is just full of bugs. To be honest, I don’t see the reason why someone should ask questions like that in the NG - after all, more or less competent programmer should be able to detect so obvious bugs without any assistance…

Anton Bassov

It’s ok to pass NULL to ObQueryNameString. ObQueryNameString will
check the pointer by itself. Anyway, pre-allocated buffer can speed up
the performance if you know the max name length in most cases.

That buffer is not interpreted in this way. It’s simply a plain
buffer. Just cast it to POBJECT_NAME_INFORMATION after setting its
length. Look at
WINDDK\6000\src\filesys\filter\sfilter\sfilter.c(6550). It clearly
shows you how to use this function.

Cheers,
R. Yang

  • Windows Kernel Developer [Custom Dev and Consulting]

On Thu, 26 Apr 2007 22:26:01 -0400 (EDT), xxxxx@hotmail.com
wrote:

// This is where the exception and PAGE_FAULT occurs
status = ObQueryNameString(object, (
POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );

You pass NULL pointer as POBJECT_NAME_INFORMATION, i.e. pointer to a structure that into which data is supposed to be copied. What else, apart from the exception, would you expect here???
It does not matter that you request 0 bytes to be returned - a pointer still has to be valid.

Here is one more “masterpiece”:

if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
0)) {

buffer = (char *)
ExAllocatePoolWithTag(PagedPool,
returnLength * sizeof(WCHAR),
‘PAGE’);

info = (POBJECT_NAME_INFORMATION) buffer;

// This is where PAGE_FAULT occurs
if (ObQueryNameString(object,
info,
returnLength,
&returnLength) ==
STATUS_INFO_LENGTH_MISMATCH) {

return NULL;
}

Look at the declaration:

typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

Therefore, you have to do something like

OBJECT_NAME_INFORMATION info;

info.Name. Buffer=ExAllocatePool(PagedPool, size);
info.Name. Length=info.Name. MaximumLength=size;

At this point you can pass ‘info’ to ObQueryNameString().

Instead of doing things this way, you just allocate a buffer, and pass it to ObQueryNameString().
How is ObQueryNameString() supposed to interpretate it???

In other words, your code is just full of bugs. To be honest, I don’t see the reason why someone should ask questions like that in the NG - after all, more or less competent programmer should be able to detect so obvious bugs without any assistance…

Anton Bassov

“Lyndon J Clarke” wrote:

I wonder are you douple-plus-sure this is pointer to pointer to object? It
would help me if you could perhaps paste a fragment of windbag session
which proves the point? I ask because, well, I’ll have to recheck my code
:slight_smile:

I checked XP, WS03 and Vista sources and it’s a single pointer in
all cases (this should be easy to verify in debugger using !object
or !pool).

I’ll file a bug to fix the docs.


This posting is provided “AS IS” with no warranties, and confers no
rights.

The sample you mention is allocating a buffer of the expected size first,
also it does not pass NULL as a buffer. It is documented that the function
will return required length if buffer is too small but where is written that
you can pass a NULL pointer as a buffer ? Even if this may work one day,
you should not rely on it unless it is documented. Generally, if you are
having problems why you don’t try things in a less provocative way first?

/Daniel

“R. Yang” wrote in message news:xxxxx@ntdev…
> It’s ok to pass NULL to ObQueryNameString. ObQueryNameString will
> check the pointer by itself. Anyway, pre-allocated buffer can speed up
> the performance if you know the max name length in most cases.
>
> That buffer is not interpreted in this way. It’s simply a plain
> buffer. Just cast it to POBJECT_NAME_INFORMATION after setting its
> length. Look at
> WINDDK\6000\src\filesys\filter\sfilter\sfilter.c(6550). It clearly
> shows you how to use this function.
>
>
> Cheers,
> R. Yang
> - Windows Kernel Developer [Custom Dev and Consulting]
>
> On Thu, 26 Apr 2007 22:26:01 -0400 (EDT), xxxxx@hotmail.com
> wrote:
>
>>// This is where the exception and PAGE_FAULT occurs
>> status = ObQueryNameString(object, (
>> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>>
>>
>>You pass NULL pointer as POBJECT_NAME_INFORMATION, i.e. pointer to a
>>structure that into which data is supposed to be copied. What else, apart
>>from the exception, would you expect here???
>>It does not matter that you request 0 bytes to be returned - a pointer
>>still has to be valid.
>>
>>Here is one more “masterpiece”:
>>
>> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>>0)) {
>>
>> buffer = (char *)
>> ExAllocatePoolWithTag(PagedPool,
>> returnLength * sizeof(WCHAR),
>>‘PAGE’);
>>
>> info = (POBJECT_NAME_INFORMATION) buffer;
>>
>> // This is where PAGE_FAULT occurs
>> if (ObQueryNameString(object,
>> info,
>> returnLength,
>> &returnLength) ==
>>STATUS_INFO_LENGTH_MISMATCH) {
>>
>> return NULL;
>> }
>>
>>Look at the declaration:
>>
>>typedef struct _OBJECT_NAME_INFORMATION {
>> UNICODE_STRING Name;
>>} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
>>
>>Therefore, you have to do something like
>>
>>OBJECT_NAME_INFORMATION info;
>>
>>info.Name. Buffer=ExAllocatePool(PagedPool, size);
>>info.Name. Length=info.Name. MaximumLength=size;
>>
>>At this point you can pass ‘info’ to ObQueryNameString().
>>
>>Instead of doing things this way, you just allocate a buffer, and pass it
>>to ObQueryNameString().
>>How is ObQueryNameString() supposed to interpretate it???
>>
>>In other words, your code is just full of bugs. To be honest, I don’t see
>>the reason why someone should ask questions like that in the NG - after
>>all, more or less competent programmer should be able to detect so obvious
>>bugs without any assistance…
>>
>>Anton Bassov
>>
>>
>

The function ObQueryNameString itself does not perform any check on the buffer being NULL or not.
What is worse is that this function may be implemented as many different functions, because there can be a specific QueryName function for each particular object.
So I would not rely at all to being able to pass a NULL as the buffer parameter.

-----Mensaje original-----
De: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]En nombre de Daniel Terhell
Enviado el: viernes, 27 de abril de 2007 12:07
Para: Windows System Software Devs Interest List
Asunto: Re:[ntdev] ObQueryNameString on registry object

The sample you mention is allocating a buffer of the expected size first,
also it does not pass NULL as a buffer. It is documented that the function
will return required length if buffer is too small but where is written that
you can pass a NULL pointer as a buffer ? Even if this may work one day,
you should not rely on it unless it is documented. Generally, if you are
having problems why you don’t try things in a less provocative way first?

/Daniel

“R. Yang” wrote in message news:xxxxx@ntdev…
> It’s ok to pass NULL to ObQueryNameString. ObQueryNameString will
> check the pointer by itself. Anyway, pre-allocated buffer can speed up
> the performance if you know the max name length in most cases.
>
> That buffer is not interpreted in this way. It’s simply a plain
> buffer. Just cast it to POBJECT_NAME_INFORMATION after setting its
> length. Look at
> WINDDK\6000\src\filesys\filter\sfilter\sfilter.c(6550). It clearly
> shows you how to use this function.
>
>
> Cheers,
> R. Yang
> - Windows Kernel Developer [Custom Dev and Consulting]
>
> On Thu, 26 Apr 2007 22:26:01 -0400 (EDT), xxxxx@hotmail.com
> wrote:
>
>>// This is where the exception and PAGE_FAULT occurs
>> status = ObQueryNameString(object, (
>> POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );
>>
>>
>>You pass NULL pointer as POBJECT_NAME_INFORMATION, i.e. pointer to a
>>structure that into which data is supposed to be copied. What else, apart
>>from the exception, would you expect here???
>>It does not matter that you request 0 bytes to be returned - a pointer
>>still has to be valid.
>>
>>Here is one more “masterpiece”:
>>
>> if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
>>0)) {
>>
>> buffer = (char *)
>> ExAllocatePoolWithTag(PagedPool,
>> returnLength * sizeof(WCHAR),
>>‘PAGE’);
>>
>> info = (POBJECT_NAME_INFORMATION) buffer;
>>
>> // This is where PAGE_FAULT occurs
>> if (ObQueryNameString(object,
>> info,
>> returnLength,
>> &returnLength) ==
>>STATUS_INFO_LENGTH_MISMATCH) {
>>
>> return NULL;
>> }
>>
>>Look at the declaration:
>>
>>typedef struct _OBJECT_NAME_INFORMATION {
>> UNICODE_STRING Name;
>>} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
>>
>>Therefore, you have to do something like
>>
>>OBJECT_NAME_INFORMATION info;
>>
>>info.Name. Buffer=ExAllocatePool(PagedPool, size);
>>info.Name. Length=info.Name. MaximumLength=size;
>>
>>At this point you can pass ‘info’ to ObQueryNameString().
>>
>>Instead of doing things this way, you just allocate a buffer, and pass it
>>to ObQueryNameString().
>>How is ObQueryNameString() supposed to interpretate it???
>>
>>In other words, your code is just full of bugs. To be honest, I don’t see
>>the reason why someone should ask questions like that in the NG - after
>>all, more or less competent programmer should be able to detect so obvious
>>bugs without any assistance…
>>
>>Anton Bassov
>>
>>
>


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

> It’s ok to pass NULL to ObQueryNameString. ObQueryNameString will check the pointer by itself.

Actually, the whole OP’s story strongly suggests exactly the opposite…

Anton Bassov

I may regret entering this fray, but the WDK definition in the header (sans return and call type declarations) for all DDI levels since W2K is:

ObQueryNameString(
__in PVOID Object,
__out_bcount_opt(Length) POBJECT_NAME_INFORMATION ObjectNameInfo,
__in ULONG Length,
__out PULONG ReturnLength
);

__out_bcount_opt(Length) says that the parameter is output only, can be NULL, and its byte length must be >= the value given in the length parameter. PFD will require length == 0 if NULL is passed.

If you pass a 0 length and NULL ObjectNameInfo pointer, and get a BSOD because of the NULL pointer, then (IMO) we have a WDK bug somewhere [even if it is just a case of correcting or adjusting the header annotation].

Hi Hao

I had a quick look over your code now. I’ll make some comments for your here
which might help.

With regard to ObQueryNameString:

  1. I never pass NULL for the ObjectNameInformation buffer. I pass in a
    buffer which is often large enough and I allocate a larger buffer if needed
    then make a second call.
  2. The ReturnLength from ObQueryNameString is the number of bytes you need
    for the entire ObjectNameInformation buffer as opposed to the number of wide
    characters needed for the actual string.
  3. There is no need to intiialize the UnicodeString fields of the
    ObjectNameInformation buffer as another poster suggests.

With regard to registry callbacks:

  1. I always treat the Object field of the PREG_XXX_INFORMATION as pointer to
    object and never as pointer to pointer to object.

Best Wishes
Lyndon

“Hao Wang” wrote in message news:xxxxx@ntdev…

Hi,

I am working on an XP-based registry call back filter driver, using the
RegistryNotifyCallback routine. I noticed that quite a few of the callbacks
(e.g., RegNtDeleteKey, RegNtPostOpenKey) either do not provide the registry
path or only give partial path. I want to locate the full registry path for
these callbacks.

After some searching on OSR, the only suggested solution, from 2004, is to
use ObQueryNameString. I implemented a method using this API, but so far
couldn’t get it to work properly. For RegNtDeleteKey, RegNtDeleteValueKey,
when I pass in the registry object pointer to ObQueryNameString, I get an
exception; for RegNtPostOpenKey, I get BSOD, with
PAGE_FAULT_IN_NONPAGED_AREA as the fault code.

I have two questions. First, is this API the correct way to obtain the
information I need? Second, if so, what did I do wrong? If not, what is the
right way?

Thanks,
Hao

Hi, all,

I didn’t expect this thread to generate so much interest :slight_smile: Thanks for
everyone to help me out with the problem!

1.) Yes, R. Yang and Pavel are correct, at least for RegNtDeleteKey and
RegNtDeleteValueKey, the Object is a POINTER to a registry object, NOT
a pointer to a pointer to a registry object as shown in MSDN.

2.) I have tried to pass in both a pre-allocated buffer as well as a
NULL buffer, which I have shown in the code I posted. And for both
methods I get a PAGE_FAULT_IN_NONPAGED_AREA fault. The debug session
attached below show the code sample where I use a buffer of size 24 for
the first ObQueryNameString call.

3.) For the debug session attached below, this is a fault generated when
my code handles RegNtPostOpenKey:

case RegNtPostOpenKey:
{
PREG_POST_OPEN_KEY_INFORMATION pInfo =
(PREG_POST_OPEN_KEY_INFORMATION) arg2;

if (pInfo != NULL) {
// Find out which key the process is trying to open, and
// cache this information. Only cache it if it was successful
if (NT_SUCCESS(pInfo->Status)) {

//
// This call causes the BSOD, line 737 of registry.c
PUNICODE_STRING fullPath = QueryObjectFullPath( pInfo->Object
);
}

And here is the code for handling QueryObjectFullPath, notice that I
have changed it to follow Anton’s suggestion, and it gives the same
result.

PUNICODE_STRING
QueryObjectFullPath(PVOID object)
{
USHORT returnLength = 0;
OBJECT_NAME_INFORMATION info;
NTSTATUS status;

if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
PUNICODE_STRING path = NULL;

info.Name.Buffer = ExAllocatePoolWithTag(PagedPool, 1024,
‘PAGE’);
info.Name.Length = 1024;
info.Name.MaximumLength = 1024;

// This is line 1310 in util.c, where the fault occurred
status = ObQueryNameString(object, &info,
sizeof(OBJECT_NAME_INFORMATION) +
1024,
&returnLength );

}

kd> !analyze -v
************************************************************************
*******
*
*
* Bugcheck Analysis
*
*
*
************************************************************************
*******

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by
try-except,
it must be protected by a Probe. Typically the address is just plain
bad or it
is pointing at freed memory.
Arguments:
Arg1: c2dec3d2, memory referenced.
Arg2: 00000000, value 0 = read operation, 1 = write operation.
Arg3: c2dec3d2, If non-zero, the instruction address which referenced
the bad memory
address.
Arg4: 00000000, (reserved)

Debugging Details:

READ_ADDRESS: c2dec3d2

FAULTING_IP:
+ffffffffc2dec3d2
c2dec3d2 ?? ???

MM_INTERNAL_CODE: 0

DEFAULT_BUCKET_ID: DRIVER_FAULT

BUGCHECK_STR: 0x50

PROCESS_NAME: explorer.exe

TRAP_FRAME: f7b5a248 – (.trap fffffffff7b5a248)
ErrCode = 00000000
eax=01000000 ebx=f7b5ac03 ecx=c2dec3d2 edx=56350101 esi=56350100
edi=f7b5ac0f
eip=c2dec3d2 esp=f7b5a2bc ebp=f7b5a3dc iopl=0 nv up ei pl zr na
pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000
efl=00010246
c2dec3d2 ?? ???
Resetting default scope

LAST_CONTROL_TRANSFER: from 80532487 to 804e3592

FAILED_INSTRUCTION_ADDRESS:
+ffffffffc2dec3d2
c2dec3d2 ?? ???

STACK_TEXT:
f7b59d98 80532487 00000004 00001000 00000000
nt!RtlpBreakWithStatusInstruction
f7b59de4 8053343b 00000004 806ee298 c030b7b0
nt!KiBugCheckDebugBreak+0x19
f7b5a1c4 8053354e 00000050 c2dec3d2 00000000 nt!KeBugCheck2+0xa51
f7b5a1e4 80523fa0 00000050 c2dec3d2 00000000 nt!KeBugCheckEx+0x1b
f7b5a230 804e1718 00000000 c2dec3d2 00000000 nt!MmAccessFault+0x6f5
f7b5a230 c2dec3d2 00000000 c2dec3d2 00000000 nt!KiTrap0E+0xcc
WARNING: Frame IP not in any known module. Following frames may be
wrong.
f7b5a2b8 80581d36 f7b5ac1c 01000000 f7b5a408 0xc2dec3d2
f7b5a3dc f77cc8ad f7b5ac1c f7b5a408 00000018 nt!ObQueryNameString+0xe0
f7b5a424 f77cd94d f7b5ac1c f742f115 e1bf7fb8
Argus!QueryObjectFullPath+0x4d [c:\starteam\research\argus\util\util.c @
1310]
f7b5a99c 80610417 81ae1be8 0000000d f7b5ab60
Argus!RegistryCallback+0xa3d
[c:\starteam\research\argus\registry_drv\registry.c @ 737]
f7b5a9d0 8060bee4 0000000d f7b5ab60 e1beead0 nt!CmpCallCallBacks+0x50
f7b5aba0 805676b5 0005d140 00000000 8195d270 nt!CmpParseKey+0x6fa
f7b5ac28 8056749a 00000058 f7b5ac68 00000040
nt!ObpLookupObjectName+0x119
f7b5ac7c 80567dfd 00000000 81bc5980 00000001 nt!ObOpenObjectByName+0xeb
f7b5ad50 804de7ec 00dffc54 00000001 00dff96c nt!NtOpenKey+0x1af
f7b5ad50 7c90eb94 00dffc54 00000001 00dff96c nt!KiFastCallEntry+0xf8
00dff948 7c90dd48 77dd6a13 00dffc54 00000001 ntdll!KiFastSystemCallRet
00dff94c 77dd6a13 00dffc54 00000001 00dff96c ntdll!ZwOpenKey+0xc
00dff9ac 77dd6b5e 00000058 00dff9d4 00000000
ADVAPI32!LocalBaseRegOpenKey+0xe4
00dff9e0 77f6412c 80000001 7c9c89b8 00000000
ADVAPI32!RegOpenKeyExW+0x10d
00dffc1c 77f64544 80000001 7c9c89b8 00000000
SHLWAPI!RegOpenKeyExWrapW+0x67
00dffd60 77f645a8 80000001 7c9c89b8 7c9c88c8 SHLWAPI!SHRegGetValueW+0xe5
00dffd84 7ca017b7 80000001 7c9c89b8 7c9c88c8 SHLWAPI!SHGetValueW+0x21
00dffdac 0102160e 000db970 00dffdd0 000eb070
SHELL32!CStartMenuPin::GetChangeCount+0x2d
00dffde0 01021647 000eaf28 010217b6 00000409
Explorer!ByUsage::_FillPinnedItemsCache+0x2b
00dffde8 010217b6 00000409 0001008e 00dffe0c
Explorer!ByUsage::PrePopulate+0xa
00dffdf8 01021897 00000000 00dffe74 01004a5c
Explorer!SFTBarHost::_EnumerateContents+0x7e
00dffe0c 7e418734 0001008e 00000409 00000000
Explorer!SFTBarHost::_WndProc+0x2ea
00dffe38 7e418816 01004a5c 0001008e 00000409
USER32!InternalCallWinProc+0x28
00dffea0 7e4189cd 0009fa60 01004a5c 0001008e
USER32!UserCallWinProcCheckWow+0x150
00dfff00 7e418a10 00dfff28 00000000 00dfff44
USER32!DispatchMessageWorker+0x306
00dfff10 01001a35 00dfff28 00000000 010460d8 USER32!DispatchMessageW+0xf
00dfff44 01011e8b 00000000 00dfffb4 77f7429a
Explorer!CTray::_MessageLoop+0xd9
00dfff50 77f7429a 010460d8 0000005c 0007fc04
Explorer!CTray::MainThreadProc+0x29
00dfffb4 7c80b683 00000000 0000005c 0007fc04
SHLWAPI!WrapperThreadProc+0x94
00dfffec 00000000 77f7422b 0007fdbc 00000000
kernel32!BaseThreadStart+0x37

STACK_COMMAND: kb

FOLLOWUP_IP:
Argus!QueryObjectFullPath+4d [c:\starteam\research\argus\util\util.c @
1310]
f77cc8ad 8945dc mov dword ptr [ebp-24h],eax

FAULTING_SOURCE_CODE:
1306: NTSTATUS status;
1307:
1308: if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
1309:

1310: status = ObQueryNameString(object,
(POBJECT_NAME_INFORMATION) buf, 24, &returnLength );
1311:
1312: if ( (status == STATUS_INFO_LENGTH_MISMATCH) &&
(returnLength > 0)) {
1313:
1314: buffer = (char *) ExAllocatePoolWithTag(PagedPool,
returnLength, ‘PAGE’);
1315:

SYMBOL_STACK_INDEX: 8

SYMBOL_NAME: Argus!QueryObjectFullPath+4d

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: Argus

IMAGE_NAME: Argus.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 46321282

FAILURE_BUCKET_ID: 0x50_CODE_AV_BAD_IP_Argus!QueryObjectFullPath+4d

BUCKET_ID: 0x50_CODE_AV_BAD_IP_Argus!QueryObjectFullPath+4d

Followup: MachineOwner

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@hotmail.com
Sent: Thursday, April 26, 2007 9:26 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] ObQueryNameString on registry object

// This is where the exception and PAGE_FAULT occurs
status = ObQueryNameString(object, (
POBJECT_NAME_INFORMATION) NULL, 0, &returnLength );

You pass NULL pointer as POBJECT_NAME_INFORMATION, i.e. pointer to a
structure that into which data is supposed to be copied. What else,
apart from the exception, would you expect here???
It does not matter that you request 0 bytes to be returned - a pointer
still has to be valid.

Here is one more “masterpiece”:

if ( (status == STATUS_INFO_LENGTH_MISMATCH) && (returnLength >
0)) {

buffer = (char *)
ExAllocatePoolWithTag(PagedPool,
returnLength * sizeof(WCHAR),
‘PAGE’);

info = (POBJECT_NAME_INFORMATION) buffer;

// This is where PAGE_FAULT occurs
if (ObQueryNameString(object,
info,
returnLength,
&returnLength) ==
STATUS_INFO_LENGTH_MISMATCH) {

return NULL;
}

Look at the declaration:

typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

Therefore, you have to do something like

OBJECT_NAME_INFORMATION info;

info.Name. Buffer=ExAllocatePool(PagedPool, size);
info.Name. Length=info.Name. MaximumLength=size;

At this point you can pass ‘info’ to ObQueryNameString().

Instead of doing things this way, you just allocate a buffer, and pass
it to ObQueryNameString().
How is ObQueryNameString() supposed to interpretate it???

In other words, your code is just full of bugs. To be honest, I don’t
see the reason why someone should ask questions like that in the NG -
after all, more or less competent programmer should be able to detect so
obvious bugs without any assistance…

Anton Bassov


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Thanks for pointing out, as long as it is written somewhere I am fine. In
the documentation there are no modifiers specified such as _opt or OPTIONAL.

/Daniel

“Bob Kjelgaard” wrote in message
news:xxxxx@ntdev…
I may regret entering this fray, but the WDK definition in the header (sans
return and call type declarations) for all DDI levels since W2K is:

ObQueryNameString(
in PVOID Object,
out_bcount_opt(Length) POBJECT_NAME_INFORMATION ObjectNameInfo,
in ULONG Length,
out PULONG ReturnLength
);

__out_bcount_opt(Length) says that the parameter is output only, can be
NULL, and its byte length must be >= the value given in the length
parameter. PFD will require length == 0 if NULL is passed.

If you pass a 0 length and NULL ObjectNameInfo pointer, and get a BSOD
because of the NULL pointer, then (IMO) we have a WDK bug somewhere [even if
it is just a case of correcting or adjusting the header annotation].