Using NT’s Undocumented Object Manager Interface



October 01, 1997
URL:http://www.drdobbs.com/using-nts-undocumented-object-manager-in/184416468

October 1997/Using NT's Undocumented Object Manager Interface


Although the Windows NT API is not object-oriented, NT uses objects internally to provide support for certain features uniformly across a variety of different entities. Internally, files, events, mutexes, pipes, and so on, are all implemented as objects. This allows NT to perform some operations, such as security checks, in a generic fashion for different types of objects. The object manager is a part of the NT executive that provides services that allow other NT subsystems to operate on objects. Most parts of these services are not currently documented, but this article describes how you can access some of them to discover information such as how many partitions a disk has.

The Object Manager Database

To allow symbolic access to NT objects, NT allows them to have names. Rather than have a single global namespace and require all object names to be unique, the NT object manager organizes object names into a hierarchy, just like the hierarchical names you give to DOS files (in fact, the file system is conceptually just a subtree of the overall NT object namespace tree). You may have run into this hierarchical namespace when, for example, creating a pipe; named pipes have names of the form \\<server>\pipe\<pipename>, where <server> is the name of the server that created the pipe (or “..” to indicate a local pipe), and <pipename> is the name of the individual pipe you want to refer to. You can think of this as though the object manager reserves a directory called “pipe” for holding the names of pipes created for any given server.

Just as a particular part of the object namespace is reserved for named pipes, other object directories in the namespace are reserved for other purposes. For example, the “Device” directory contains a list of objects that represent devices, such as “Floppy0”, “HardDisk0”, and so on. A variety of interesting information is available, if you could just traverse the object manager’s namespace.

The object manager also supports symbolic links. As the name implies, these are place holders that refer to other objects in the namespace tree. Symbolic links allow NT to make the same object available under different names. For example, NT may have named your floppy drive “\Device\Floppy0”, but by creating a symbolic link called “A:” that refers to it, you can use familiar DOS names to access the floppy drive.

The namespace is created dynamically by the object manager at runtime, so there’s no disk file you can use to traverse it. Instead, you have to find a way to access the API that the object manager provides for other NT subsystems.

Viewing the Object Manager Namespace

There are some user-level tools available to view the namespace. ObjDir is part of the DDK and WinObj is available from www.ntinternals.com. But if you look in the DDK documentation you will notice that functions to access the namespace are not documented. Sometimes it is useful to programmatically find out which drivers are installed or how many disk partitions are available.

If you look at the exported symbols of ntdll.dll (e.g., by using DUMPBIN, which is part of MS VC 4.x) you will find lots of functions that are not documented. Of particular interest are NtOpenDirectoryObject(), NtQueryDirectoryObject(), NtOpenSymbolicLinkObject(), and NtQuerySymbolicLinkObject(). When you peek into ntdll.lib (the import library of ntdll.dll) with a hex editor, you find a hint for the number of parameters the functions expect. Visual C++ demands export libraries that use mangled names, even if the names in the corresponding DLL are not mangled, and the mangling includes an indication of the number of bytes of parameters the function takes. The number after the “@” of the mangled symbol name is the total size in bytes of all parameters.

In Win32 every parameter is at least four bytes (there are 64-bit parameters, but not here). Almost all Windows NT executive functions return a status code, so the four functions here would probably do so, too. For the two NTOpenXxxx() functions, it was easy to guess that they have parameters similar to the documented functions in the DDK that open objects. Interestingly, the access flags for directory and symbolic link objects are already in ntddk.h. Discovering the arguments to NtQueryDirectoryObject() was not that easy, but you can find out what the parameters may mean by disassembling, for example, ObjDir and then playing around with the function.

NtQueryDirectoryObject() is used to get the name and type of the objects in a directory object. A specific object is referenced by a zero-based index. An error is returned if the index is too large. The type of an object is a string such as “Device”, “Directory”, “SymbolicLink”, and so on. For the additional parameters of this function see the comments in objlst.h (Listing 1).

In a similar way, you can find out about NtQuerySymbolicLinkObject(), which is easier because it has fewer parameters. The function resolves a symbolic link and returns the name of the object to which the link points.

The header file objlst.h shows the prototypes for the functions. The naming convention and types are similar to ntddk.h of the DDK. You may have noticed that there are similar functions in the export list of ntdll.dll that start with ZwXxxx instead of NtXxxx. These functions differ only in the way they do internal parameter checking. The ZwXxxx functions are called from device drivers and the parameters are not checked, whereas the NtXxxx functions are called from subsystems and the parameters are validated (detailed parameter checking depends on the mode from which a function is called; NtXxxx functions always set the previous mode to user mode).

An Example

The sample program objlst.c (Listing 2) is a simple tool which uses the four functions. It recursively lists the directories of the Object Manager. DumpAllObjects() is the workhorse; it traverses a directory, resolves symbolic links if necessary, and recurses into other directories if it finds one. An optional parameter for the program specifies the directory to start with (e.g., “OBJLST \Device” to list the device directory).

objlst.c requires ntddk.h (and all included headers) and ntdll.lib from the DDK. It shows how to write a simple Win32 console application that uses Windows NT executive functions and that can be compiled without using the standard DDK build tool. Of course, you can use the functions in a kernel-mode driver also. You can also use the ZwXxxx functions instead of the NtXxxx counterparts.

Note that a Microsoft-specific extension of the format strings for the printf() function class is used. “%Z” and “%wZ” are used to print PANSI_STRING and PUNICODE_STRING parameters. PANSI_STRING and PUNICODE_STRING do not directly point to the string buffer — they point to a structure which contains the number of characters in the buffer, the maximum length of the buffer, and a pointer to the buffer itself. The string in the buffer is not guaranteed to be zero-terminated.

Conclusion

Sometimes you need to use undocumented functions to solve a problem. There is always the issue of whether it is safe to use them. Of course, they are not supported by Microsoft and they may change without notice in the next release or service pack (NT executive functions probably won’t). But you can perform many interesting tasks which are otherwise nearly impossible. There are still a lot of things to uncover in Windows NT.

Dieter Spaar is a self-employed software developer doing high-level and low-level programming since 1990. When not working for clients, he relaxes by uncovering undocumented features in various pieces of software. Dieter can be reached at [email protected].

Get Source Code

October 1997/Using NT's Undocumented Object Manager Interface/Listing 1

Listing 1: objlst.h — Interface to NT object manager

//
// Author: Dieter Spaar <[email protected]>
// June 1997

// Object directory information returned by
// NtQueryDirectoryObject()

typedef struct _OBJDIR_INFORMATION
{
  UNICODE_STRING ObjectName;
  UNICODE_STRING ObjectTypeName; // e.g. Directory, Device ...
  UCHAR          Data[1];        // variable length
} OBJDIR_INFORMATION, *POBJDIR_INFORMATION;

// Open a directory object
//   Object Manager directory specific access rights are in
//   NTDDK.H e.g. DIRECTORY_QUERY

NTSYSAPI NTSTATUS NTAPI 
  NtOpenDirectoryObject(
    OUT PHANDLE             DirObjHandle, 
    IN ACCESS_MASK          DesiredAccess, 
    IN POBJECT_ATTRIBUTES   ObjectAttributes);

// Query information about the content of a directory object
//
//   DirObjInformation   buffer must be large enough to hold
//                       the name strings too
//
//   GetNextIndex=TRUE   return the index of the next object
//                       in this directory in ObjectIndex
//   GetNextIndex=FALSE  return the number of objects in
//                       this directory in ObjectIndex
//
//   IgnoreInputIndex=TRUE  ignore input value of ObjectIndex
//                          always start at index 0
//   IgnoreInputIndex=FALSE use input value of ObjectIndex
//
//   ObjectIndex  zero based index of object in the directory
//                depends on GetNextIndex and IgnoreInputIndex

NTSYSAPI NTSTATUS NTAPI 
  NtQueryDirectoryObject(
    IN HANDLE DirObjHandle,
    OUT POBJDIR_INFORMATION DirObjInformation, 
    IN ULONG                BufferLength, // size of info buffer
    IN BOOLEAN              GetNextIndex, 
    IN BOOLEAN              IgnoreInputIndex, 
    IN OUT PULONG           ObjectIndex,
    OUT PULONG              DataWritten); // can be NULL

// Open a symbolic link object
//   Object Manager symbolic link specific access rights are in
//   NTDDK.H e.g. SYMBOLIC_LINK_QUERY

NTSYSAPI NTSTATUS NTAPI 
  NtOpenSymbolicLinkObject(
    OUT PHANDLE             SymLinkObjHandle,
    IN ACCESS_MASK          DesiredAccess, 
    IN POBJECT_ATTRIBUTES   ObjectAttributes);

// Resolve a sysmbolic link

NTSYSAPI NTSTATUS NTAPI 
  NtQuerySymbolicLinkObject(
    IN HANDLE               SymLinkObjHandle,
    OUT PUNICODE_STRING     LinkName,     // resolved name of link
    OUT PULONG              DataWritten); // can be NULL

// Close a handle (not specific to Object Manager)

NTSYSAPI NTSTATUS NTAPI 
  NtClose(
    IN HANDLE               ObjectHandle);
//End of File
October 1997/Using NT's Undocumented Object Manager Interface/Listing 2

Listing 2: objlst.c — Sample program to scan object directory

//
// Author: Dieter Spaar <[email protected]>
// June 1997

#define _X86_                1  // otherwise lots of symbols
                                // in NTDDK.H are not defined

// MS format extensions for printf function family
// (may not work with other compilers)
//
//                 %Z     parameter is PANSI_STRING
//                 %wZ    parameter is PUNICODE_STRING

#include <ntddk.h>
#include <stdio.h>

#include "objlst.h"

void DumpAllObjects(WCHAR *pszDir)
{
  HANDLE hObj, hLink;
  NTSTATUS ntStatus, ntStatusTmp;
  OBJECT_ATTRIBUTES ObjectAttributes;
  POBJDIR_INFORMATION DirObjInformation;
  UNICODE_STRING UName;
  char szData[1024*2];
  char szBuf[1024*2];
  WCHAR szLinkName[1024];
  ULONG dw;
  char szIdentBuf[100];
  ULONG index;
  static int iLevel = 0;

  // set indent of current level

  memset(szIdentBuf, ' ', iLevel * 3);
  szIdentBuf[iLevel * 3] = 0;

  // open directory object

  RtlInitUnicodeString(&UName, pszDir);

  InitializeObjectAttributes (
                  &ObjectAttributes,
                  &UName,
                  OBJ_CASE_INSENSITIVE,
                  NULL,
                  NULL );

  ntStatus = NtOpenDirectoryObject(
                  &hObj, 
                  STANDARD_RIGHTS_READ | 
                  DIRECTORY_QUERY, 
                  &ObjectAttributes);

  if(NT_SUCCESS(ntStatus))
  {
    index = 0; // start index
    
    do
    {
      memset(szData, 0, sizeof(szData));
      DirObjInformation = (POBJDIR_INFORMATION)&szData;
      ntStatus = NtQueryDirectoryObject(
                      hObj, 
                      DirObjInformation, 
                      sizeof(szData), 
                      TRUE,         // get next index
                      FALSE,        // don't ignore index input
                      &index, 
                      &dw);         // can be NULL

      if(NT_SUCCESS(ntStatus))
      {
        szBuf[0] = 0;
        if(wcscmp(DirObjInformation->ObjectTypeName.Buffer, 
                  L"SymbolicLink") == 0)
        {
          // handle symbolic links

          // concatenate directory and name, insert \ if needed

          if(wcslen(pszDir) && 
            pszDir[wcslen(pszDir) - 1] == (WCHAR)'\\')
          {
            swprintf(szLinkName, L"%s%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }
          else
          {
            swprintf(szLinkName, L"%s\\%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }

          // open sysmbolic link object

          RtlInitUnicodeString(&UName, szLinkName);
          InitializeObjectAttributes (
                        &ObjectAttributes,
                        &UName,
                        OBJ_CASE_INSENSITIVE,
                        NULL,
                        NULL);

          ntStatusTmp = NtOpenSymbolicLinkObject(
                        &hLink, 
                        SYMBOLIC_LINK_QUERY, 
                        &ObjectAttributes);

          if(NT_SUCCESS(ntStatusTmp))
          {
            UName.Length = 0;
            UName.MaximumLength = sizeof(szLinkName);
            UName.Buffer = szLinkName;
            ntStatusTmp = NtQuerySymbolicLinkObject(
                          hLink, 
                          &UName, 
                          &dw); // can be NULL
        
            if(NT_SUCCESS(ntStatusTmp))
              sprintf(szBuf, "--> '%wZ'", &UName);
            else
              printf("NtQuerySymbolicLinkObject = 0x%lX\n", 
                        ntStatusTmp);

            NtClose(hLink);
          }
          else
            printf("NtOpenSymboliclinkObject = 0x%lX\n", 
                      ntStatusTmp);
        }

        // print information for one object

        printf("%s%-15wZ '%wZ' %s\n", szIdentBuf, 
                &DirObjInformation->ObjectTypeName, 
                &DirObjInformation->ObjectName, 
                szBuf);

        if(wcscmp(DirObjInformation->ObjectTypeName.Buffer, 
                  L"Directory") == 0)
        {
          // handle directories

          iLevel++;

          // concatenate directory and name, insert \ if needed

          if(wcslen(pszDir) && 
             pszDir[wcslen(pszDir) - 1] == (WCHAR)'\\')
          {
            swprintf((WCHAR*)szBuf, L"%s%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }
          else
          {
            swprintf((WCHAR*)szBuf, L"%s\\%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }

          // recurse into next directory

          DumpAllObjects((WCHAR*)szBuf);

          iLevel--;
        }
      }
      else if(NT_ERROR(ntStatus))
        printf("NtQueryDirectoryObject = 0x%lX (%S)\n", ntStatus, 
                pszDir);
    }
    while(NT_SUCCESS(ntStatus));

    NtClose(hObj);
  }
  else
    printf("NtOpenDirectoryObject = 0x%lX (%S)\n", ntStatus, 
            pszDir);
}

int main(int argc, char **argv, char **envp)
{
  WCHAR szBuf[1024*2];

  if(argc == 1) // no parameter, start at root
    DumpAllObjects(L"\\");
  else
  {
    swprintf(szBuf, L"%S", argv[1]); // convert to UNICODE
    DumpAllObjects(szBuf);
  }
  return 0;
}
//End of File
October 1997/Using NT's Undocumented Object Manager Interface

Using NT’s Undocumented Object Manager Interface

Dieter Spaar


Although the Windows NT API is not object-oriented, NT uses objects internally to provide support for certain features uniformly across a variety of different entities. Internally, files, events, mutexes, pipes, and so on, are all implemented as objects. This allows NT to perform some operations, such as security checks, in a generic fashion for different types of objects. The object manager is a part of the NT executive that provides services that allow other NT subsystems to operate on objects. Most parts of these services are not currently documented, but this article describes how you can access some of them to discover information such as how many partitions a disk has.

The Object Manager Database

To allow symbolic access to NT objects, NT allows them to have names. Rather than have a single global namespace and require all object names to be unique, the NT object manager organizes object names into a hierarchy, just like the hierarchical names you give to DOS files (in fact, the file system is conceptually just a subtree of the overall NT object namespace tree). You may have run into this hierarchical namespace when, for example, creating a pipe; named pipes have names of the form \\<server>\pipe\<pipename>, where <server> is the name of the server that created the pipe (or “..” to indicate a local pipe), and <pipename> is the name of the individual pipe you want to refer to. You can think of this as though the object manager reserves a directory called “pipe” for holding the names of pipes created for any given server.

Just as a particular part of the object namespace is reserved for named pipes, other object directories in the namespace are reserved for other purposes. For example, the “Device” directory contains a list of objects that represent devices, such as “Floppy0”, “HardDisk0”, and so on. A variety of interesting information is available, if you could just traverse the object manager’s namespace.

The object manager also supports symbolic links. As the name implies, these are place holders that refer to other objects in the namespace tree. Symbolic links allow NT to make the same object available under different names. For example, NT may have named your floppy drive “\Device\Floppy0”, but by creating a symbolic link called “A:” that refers to it, you can use familiar DOS names to access the floppy drive.

The namespace is created dynamically by the object manager at runtime, so there’s no disk file you can use to traverse it. Instead, you have to find a way to access the API that the object manager provides for other NT subsystems.

Viewing the Object Manager Namespace

There are some user-level tools available to view the namespace. ObjDir is part of the DDK and WinObj is available from www.ntinternals.com. But if you look in the DDK documentation you will notice that functions to access the namespace are not documented. Sometimes it is useful to programmatically find out which drivers are installed or how many disk partitions are available.

If you look at the exported symbols of ntdll.dll (e.g., by using DUMPBIN, which is part of MS VC 4.x) you will find lots of functions that are not documented. Of particular interest are NtOpenDirectoryObject(), NtQueryDirectoryObject(), NtOpenSymbolicLinkObject(), and NtQuerySymbolicLinkObject(). When you peek into ntdll.lib (the import library of ntdll.dll) with a hex editor, you find a hint for the number of parameters the functions expect. Visual C++ demands export libraries that use mangled names, even if the names in the corresponding DLL are not mangled, and the mangling includes an indication of the number of bytes of parameters the function takes. The number after the “@” of the mangled symbol name is the total size in bytes of all parameters.

In Win32 every parameter is at least four bytes (there are 64-bit parameters, but not here). Almost all Windows NT executive functions return a status code, so the four functions here would probably do so, too. For the two NTOpenXxxx() functions, it was easy to guess that they have parameters similar to the documented functions in the DDK that open objects. Interestingly, the access flags for directory and symbolic link objects are already in ntddk.h. Discovering the arguments to NtQueryDirectoryObject() was not that easy, but you can find out what the parameters may mean by disassembling, for example, ObjDir and then playing around with the function.

NtQueryDirectoryObject() is used to get the name and type of the objects in a directory object. A specific object is referenced by a zero-based index. An error is returned if the index is too large. The type of an object is a string such as “Device”, “Directory”, “SymbolicLink”, and so on. For the additional parameters of this function see the comments in objlst.h (Listing 1).

In a similar way, you can find out about NtQuerySymbolicLinkObject(), which is easier because it has fewer parameters. The function resolves a symbolic link and returns the name of the object to which the link points.

The header file objlst.h shows the prototypes for the functions. The naming convention and types are similar to ntddk.h of the DDK. You may have noticed that there are similar functions in the export list of ntdll.dll that start with ZwXxxx instead of NtXxxx. These functions differ only in the way they do internal parameter checking. The ZwXxxx functions are called from device drivers and the parameters are not checked, whereas the NtXxxx functions are called from subsystems and the parameters are validated (detailed parameter checking depends on the mode from which a function is called; NtXxxx functions always set the previous mode to user mode).

An Example

The sample program objlst.c (Listing 2) is a simple tool which uses the four functions. It recursively lists the directories of the Object Manager. DumpAllObjects() is the workhorse; it traverses a directory, resolves symbolic links if necessary, and recurses into other directories if it finds one. An optional parameter for the program specifies the directory to start with (e.g., “OBJLST \Device” to list the device directory).

objlst.c requires ntddk.h (and all included headers) and ntdll.lib from the DDK. It shows how to write a simple Win32 console application that uses Windows NT executive functions and that can be compiled without using the standard DDK build tool. Of course, you can use the functions in a kernel-mode driver also. You can also use the ZwXxxx functions instead of the NtXxxx counterparts.

Note that a Microsoft-specific extension of the format strings for the printf() function class is used. “%Z” and “%wZ” are used to print PANSI_STRING and PUNICODE_STRING parameters. PANSI_STRING and PUNICODE_STRING do not directly point to the string buffer — they point to a structure which contains the number of characters in the buffer, the maximum length of the buffer, and a pointer to the buffer itself. The string in the buffer is not guaranteed to be zero-terminated.

Conclusion

Sometimes you need to use undocumented functions to solve a problem. There is always the issue of whether it is safe to use them. Of course, they are not supported by Microsoft and they may change without notice in the next release or service pack (NT executive functions probably won’t). But you can perform many interesting tasks which are otherwise nearly impossible. There are still a lot of things to uncover in Windows NT.

Dieter Spaar is a self-employed software developer doing high-level and low-level programming since 1990. When not working for clients, he relaxes by uncovering undocumented features in various pieces of software. Dieter can be reached at [email protected].

Get Source Code

October 1997/Using NT's Undocumented Object Manager Interface/Listing 1

Listing 1: objlst.h — Interface to NT object manager

//
// Author: Dieter Spaar <[email protected]>
// June 1997

// Object directory information returned by
// NtQueryDirectoryObject()

typedef struct _OBJDIR_INFORMATION
{
  UNICODE_STRING ObjectName;
  UNICODE_STRING ObjectTypeName; // e.g. Directory, Device ...
  UCHAR          Data[1];        // variable length
} OBJDIR_INFORMATION, *POBJDIR_INFORMATION;

// Open a directory object
//   Object Manager directory specific access rights are in
//   NTDDK.H e.g. DIRECTORY_QUERY

NTSYSAPI NTSTATUS NTAPI 
  NtOpenDirectoryObject(
    OUT PHANDLE             DirObjHandle, 
    IN ACCESS_MASK          DesiredAccess, 
    IN POBJECT_ATTRIBUTES   ObjectAttributes);

// Query information about the content of a directory object
//
//   DirObjInformation   buffer must be large enough to hold
//                       the name strings too
//
//   GetNextIndex=TRUE   return the index of the next object
//                       in this directory in ObjectIndex
//   GetNextIndex=FALSE  return the number of objects in
//                       this directory in ObjectIndex
//
//   IgnoreInputIndex=TRUE  ignore input value of ObjectIndex
//                          always start at index 0
//   IgnoreInputIndex=FALSE use input value of ObjectIndex
//
//   ObjectIndex  zero based index of object in the directory
//                depends on GetNextIndex and IgnoreInputIndex

NTSYSAPI NTSTATUS NTAPI 
  NtQueryDirectoryObject(
    IN HANDLE DirObjHandle,
    OUT POBJDIR_INFORMATION DirObjInformation, 
    IN ULONG                BufferLength, // size of info buffer
    IN BOOLEAN              GetNextIndex, 
    IN BOOLEAN              IgnoreInputIndex, 
    IN OUT PULONG           ObjectIndex,
    OUT PULONG              DataWritten); // can be NULL

// Open a symbolic link object
//   Object Manager symbolic link specific access rights are in
//   NTDDK.H e.g. SYMBOLIC_LINK_QUERY

NTSYSAPI NTSTATUS NTAPI 
  NtOpenSymbolicLinkObject(
    OUT PHANDLE             SymLinkObjHandle,
    IN ACCESS_MASK          DesiredAccess, 
    IN POBJECT_ATTRIBUTES   ObjectAttributes);

// Resolve a sysmbolic link

NTSYSAPI NTSTATUS NTAPI 
  NtQuerySymbolicLinkObject(
    IN HANDLE               SymLinkObjHandle,
    OUT PUNICODE_STRING     LinkName,     // resolved name of link
    OUT PULONG              DataWritten); // can be NULL

// Close a handle (not specific to Object Manager)

NTSYSAPI NTSTATUS NTAPI 
  NtClose(
    IN HANDLE               ObjectHandle);
//End of File
October 1997/Using NT's Undocumented Object Manager Interface/Listing 2

Listing 2: objlst.c — Sample program to scan object directory

//
// Author: Dieter Spaar <[email protected]>
// June 1997

#define _X86_                1  // otherwise lots of symbols
                                // in NTDDK.H are not defined

// MS format extensions for printf function family
// (may not work with other compilers)
//
//                 %Z     parameter is PANSI_STRING
//                 %wZ    parameter is PUNICODE_STRING

#include <ntddk.h>
#include <stdio.h>

#include "objlst.h"

void DumpAllObjects(WCHAR *pszDir)
{
  HANDLE hObj, hLink;
  NTSTATUS ntStatus, ntStatusTmp;
  OBJECT_ATTRIBUTES ObjectAttributes;
  POBJDIR_INFORMATION DirObjInformation;
  UNICODE_STRING UName;
  char szData[1024*2];
  char szBuf[1024*2];
  WCHAR szLinkName[1024];
  ULONG dw;
  char szIdentBuf[100];
  ULONG index;
  static int iLevel = 0;

  // set indent of current level

  memset(szIdentBuf, ' ', iLevel * 3);
  szIdentBuf[iLevel * 3] = 0;

  // open directory object

  RtlInitUnicodeString(&UName, pszDir);

  InitializeObjectAttributes (
                  &ObjectAttributes,
                  &UName,
                  OBJ_CASE_INSENSITIVE,
                  NULL,
                  NULL );

  ntStatus = NtOpenDirectoryObject(
                  &hObj, 
                  STANDARD_RIGHTS_READ | 
                  DIRECTORY_QUERY, 
                  &ObjectAttributes);

  if(NT_SUCCESS(ntStatus))
  {
    index = 0; // start index
    
    do
    {
      memset(szData, 0, sizeof(szData));
      DirObjInformation = (POBJDIR_INFORMATION)&szData;
      ntStatus = NtQueryDirectoryObject(
                      hObj, 
                      DirObjInformation, 
                      sizeof(szData), 
                      TRUE,         // get next index
                      FALSE,        // don't ignore index input
                      &index, 
                      &dw);         // can be NULL

      if(NT_SUCCESS(ntStatus))
      {
        szBuf[0] = 0;
        if(wcscmp(DirObjInformation->ObjectTypeName.Buffer, 
                  L"SymbolicLink") == 0)
        {
          // handle symbolic links

          // concatenate directory and name, insert \ if needed

          if(wcslen(pszDir) && 
            pszDir[wcslen(pszDir) - 1] == (WCHAR)'\\')
          {
            swprintf(szLinkName, L"%s%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }
          else
          {
            swprintf(szLinkName, L"%s\\%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }

          // open sysmbolic link object

          RtlInitUnicodeString(&UName, szLinkName);
          InitializeObjectAttributes (
                        &ObjectAttributes,
                        &UName,
                        OBJ_CASE_INSENSITIVE,
                        NULL,
                        NULL);

          ntStatusTmp = NtOpenSymbolicLinkObject(
                        &hLink, 
                        SYMBOLIC_LINK_QUERY, 
                        &ObjectAttributes);

          if(NT_SUCCESS(ntStatusTmp))
          {
            UName.Length = 0;
            UName.MaximumLength = sizeof(szLinkName);
            UName.Buffer = szLinkName;
            ntStatusTmp = NtQuerySymbolicLinkObject(
                          hLink, 
                          &UName, 
                          &dw); // can be NULL
        
            if(NT_SUCCESS(ntStatusTmp))
              sprintf(szBuf, "--> '%wZ'", &UName);
            else
              printf("NtQuerySymbolicLinkObject = 0x%lX\n", 
                        ntStatusTmp);

            NtClose(hLink);
          }
          else
            printf("NtOpenSymboliclinkObject = 0x%lX\n", 
                      ntStatusTmp);
        }

        // print information for one object

        printf("%s%-15wZ '%wZ' %s\n", szIdentBuf, 
                &DirObjInformation->ObjectTypeName, 
                &DirObjInformation->ObjectName, 
                szBuf);

        if(wcscmp(DirObjInformation->ObjectTypeName.Buffer, 
                  L"Directory") == 0)
        {
          // handle directories

          iLevel++;

          // concatenate directory and name, insert \ if needed

          if(wcslen(pszDir) && 
             pszDir[wcslen(pszDir) - 1] == (WCHAR)'\\')
          {
            swprintf((WCHAR*)szBuf, L"%s%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }
          else
          {
            swprintf((WCHAR*)szBuf, L"%s\\%wZ", pszDir, 
                     &DirObjInformation->ObjectName);
          }

          // recurse into next directory

          DumpAllObjects((WCHAR*)szBuf);

          iLevel--;
        }
      }
      else if(NT_ERROR(ntStatus))
        printf("NtQueryDirectoryObject = 0x%lX (%S)\n", ntStatus, 
                pszDir);
    }
    while(NT_SUCCESS(ntStatus));

    NtClose(hObj);
  }
  else
    printf("NtOpenDirectoryObject = 0x%lX (%S)\n", ntStatus, 
            pszDir);
}

int main(int argc, char **argv, char **envp)
{
  WCHAR szBuf[1024*2];

  if(argc == 1) // no parameter, start at root
    DumpAllObjects(L"\\");
  else
  {
    swprintf(szBuf, L"%S", argv[1]); // convert to UNICODE
    DumpAllObjects(szBuf);
  }
  return 0;
}
//End of File

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.