Creating a “WinObj”-like Tool

February 5, 2014

17 comments

The SysInternals WinObj tool allows looking into the Object Manager’s namespace:

clip_image002[5]

The left view looks like file system folders, but in fact these are logical folders maintained by the Object Manager (part of the Executive within the kernel) purely in memory. I will not get into details about the information itself that is provided by the tool in this post. You can find some information on the web and the book “The SysInternals Administrative Reference”.

How does WinObj gets the information? One obvious way is to use a driver – in kernel mode everything is accessible – so the client app can get the required information by communicating with its own driver. WinObj does not use a driver, however (this is one reason it’s able to execute without admin privileges, although with admin privileges it shows all objects as opposed to partial results).

Instead, WinObj uses undocumented Native API (in NTDLL.Dll) functions to enumerate the various folders and objects. These functions call their kernel counterparts through the usual NTDLL mechanism (sysenter / syscall CPU instructions). It turns out, that the same functions are implemented in the kernel, with the same arguments – and so we have at least partial documentation in the Windows Driver Kit (WDK).

I decided to create my own version of WinObj, perhaps with some improvements (such as nicer iconsSmile), but mostly for fun and the learning experience.

Enumerating the Objects

To get a list of objects (including directories) from some root directory, we need to call NtOpenDirectoryObject and then NtQueryDirectoryObject. The “Directory” part does not mean it’s related to some file system – this is a logical directory maintained by the object manager.

The WDK documentation for NtOpenDirectoryObject states that drivers should use ZwOpenDirectoryObject (the difference between the Nt* variants and the Zw* variants is basically this: Nt* variants go through security checks – after all, they are typically invoked because of calls from user mode, while Zw* calls don’t). ZwOpenDirectoryObject is documented and is prototyped like so:

NTSTATUS ZwOpenDirectoryObject(

   _Out_  PHANDLE DirectoryHandle,

   _In_   ACCESS_MASK DesiredAccess,

   _In_   POBJECT_ATTRIBUTES ObjectAttributes

);

The returned value is a standard NTSTATUS, which is just a 32 bit number, and in fact has the same format as a COM HRESULT – negative values indicate failure.

The returned directory handle is the first argument, while the second argument states what kind of access the caller wishes to have; several are defined such as DIRECTORY_QUERY and DIRECTORY_TRAVERSE. The last argument is a pointer to an OBJECT_ATTRIBUTES structure, which (among other things) contains the name of the directory to open.

Taking this prototype and turning it into an NTDLL function makes it look like this:

NTSTATUS NTAPI NtOpenDirectoryObject(
        OUT PHANDLE hDirectory,
        IN ACCESS_MASK AccessMask,
        IN POBJECT_ATTRIBUTES ObjectAttributes
);

The arguments are the same, the name changed to begin with “Nt” instead of “Zw” and the NTAPI macro is used, which just means __stdcall, the standard calling convention, which is not the default in regular C++ user mode projects (__cdecl is the default).

Here’s the call I used to open a directory object provided in a Unicode string variable named root:

HANDLE hDirectory;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING name;
RtlInitUnicodeString(&name, root);
InitializeObjectAttributes(&attr, &name, 0, nullptr, nullptr);
NTSTATUS status = NtOpenDirectoryObject(&hDirectory, DIRECTORY_QUERY | 
   DIRECTORY_TRAVERSE, &attr);

The UNICODE_STRING structure is the standard way to represent string within the kernel and is used by NTDLL as well – I just copied its declaration from the appropriate WDK header. Basically, it’s a Unicode string pointer (named Buffer) with a Length and MaximumLength members.

The InitializeObjectAttributes macro sets up the OBJECT_ATTRIBUTES structure (again, used heavily in kernel code and NTDLL), specifying the directory “path” to open.

To actually link successfully, we must add ntdll.lib to the project, or via source:

#pragma comment(lib, "ntdll.lib")

Fortunately, this LIB file is available with Visual Studio and the Windows SDK. If it wasn’t, we would have to resort to GetProcAddress calls to dynamically obtain pointers to every function we use from NTDLL.

Once that’s done, the next step is to get the actual directory contents via NtQueryDirectoryObject. This is, unfortunately, undocumented, even in the WDK. Fortunately, there is reasonable documentation in the site http://undocumented.ntinternals.net (with many other native functions). Here’s the call I make to get object information within a directory:

vector<pair<CString, CString>> list;
ULONG index = 0, bytes;
do {
   status = NtQueryDirectoryObject(hDirectory, _buffer, _size, 
      FALSE, TRUE, &index, &bytes);
   if(status < 0)
      break;
   for(ULONG i = 0; i < index; i++)
      list.push_back(make_pair(_buffer[i].Name.Buffer, 
         _buffer[i].TypeName.Buffer));
      if(bytes <= _size)
         break;
} while(true);

The _buffer argument is types as OBJECT_DIRECTORY_INFORMATION*, defined like so:

typedef struct _OBJECT_DIRECTORY_INFORMATION {
        UNICODE_STRING Name;
        UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION;

This structure provides an objects name and its type (the type being something like “Event”, “Mutant”, “Semaphore”, “Process”, etc.). The buffer is allocated with some size (_size) that may contain a list of such objects; since each object name is a UNICODE_STRING, the actual string is stored immediately following the structures. If the allocated buffer is insufficient, the call is made again to get the next batch of objects.

The loop iterates through all the objects and builds a std::vector of std::pair objects, each consisting of the object’s name and its type name. This is later used to display the information in the UI. Here’s a screenshot of my version of WinObj (named Object Manager Browser):

clip_image004

Symbolic link objects are checked for specifically, so that the “Link” column can be displayed directly within the UI. This is done with another pair of native function, NtOpenSymbolicLinkObject and NtQuerySymbolicLinkObject:

RtlInitUnicodeString(&str, name);
InitializeObjectAttributes(&attr, &str, 0, hRoot, nullptr);
NtOpenSymbolicLinkObject(&hLink, GENERIC_READ, &attr);
WCHAR buffer[256] = { 0 };
UNICODE_STRING target;
RtlInitUnicodeString(&target, buffer);
target.MaximumLength = 255 * 2;
ULONG len;
NtQuerySymbolicLinkObject(hLink, &target, &len);
CloseHandle(hLink);

The variable hRoot is an open handle to the directory in which this symbolic link resides; name is the symbolic link object’s name and the variable target is the one receiving the resulting symbolic link.

Features and features-to-be

Currently the tool provides the basic directory/object information, similar to WinObj. It has more icons for object types and allows copying the selected object’s name to the clipboard. The main missing feature relative to WinObj is the properties dialog for an object; this is be my next step.

I plan to release the source once I finish the complete tool. For now, you can enjoy it in its current state by downloading from this link. As a reminder, running the tool with admin privileges shows the complete object tree (with complete contents).

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

17 comments

  1. Pingback: Creating an Object Manager Browser Part 2–Viewing Object Information | Pavel's Blog

  2. sanjayApril 8, 2014 ב 05:53

    I added the wdk library and include folder in the project settings
    And then I included ntddk.h I’m getting lot of errors and which header file to include to use QueryDirectoryObject

    Reply
    1. Pavel Yosifovich
      Pavel YosifovichApril 9, 2014 ב 14:20

      You can’t really include ntddk.h, because we’re writing a user mode application. We can include parts that exist in NTDLL only.

      Reply
      1. RussellAugust 17, 2014 ב 00:10

        Did you ever release the source of your WinObj like tool?

        Reply
        1. Pavel Yosifovich
          Pavel YosifovichAugust 17, 2014 ב 07:48

          Yes, you can find it here: http://1drv.ms/1ejjCcY

          Reply
          1. itotsufutatsuMay 16, 2016 ב 16:34

            Can you please re upload it?
            It is broken :<

          2. Pavel Yosifovich
            Pavel YosifovichMay 9, 2017 ב 11:25

            The repo is now on github http://github.cm/zodiacon

  3. Alexander RiccioJanuary 10, 2015 ב 01:00

    I’m curious, why is InitializeObjectAttributes a macro, and not an inline function? i.e.

    void InitializeObjectAttributes_f( _Out_ POBJECT_ATTRIBUTES InitializedAttributes, _In_ PUNICODE_STRING ObjectName, _In_ ULONG Attributes, _In_ HANDLE RootDirectory, _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor ) {
    InitializedAttributes->Length = sizeof( OBJECT_ATTRIBUTES );
    InitializedAttributes->RootDirectory = RootDirectory;
    InitializedAttributes->Attributes = Attributes;
    InitializedAttributes->ObjectName = ObjectName;
    InitializedAttributes->SecurityDescriptor = SecurityDescriptor;
    InitializedAttributes->SecurityQualityOfService = NULL;
    }

    Reply
    1. Pavel Yosifovich
      Pavel YosifovichJanuary 10, 2015 ב 12:19

      This is C, not C++. There are no inline functions, so macros must be used.

      Reply
      1. Shane BurkeJune 1, 2016 ב 00:34

        The source code for WinObj is no longer available on the OneDrive link. Is there another place where the source code can be downloaded?

        Reply
          1. Shane BurkeJune 1, 2016 ב 16:44

            Thanks for the repost. I was able to pull down the repository. However, NtDll.h, SecurityInformation.h, and SecruityInformation.cpp are missing from the archive. Can these be downloaded elsewhere? Thanks again.

          2. Shane BurkeJune 1, 2016 ב 22:56

            Typo correction: I intended to say SecurityInformation.cpp.

    2. Pavel Yosifovich
      Pavel YosifovichJune 2, 2016 ב 10:22

      Mostly because it’s such an old function, inline was not really available back then

      Reply
  4. Pavel Yosifovich
    Pavel YosifovichJune 2, 2016 ב 10:25

    Shane – I added those, please try again

    Reply
    1. Shane BurkeJune 2, 2016 ב 16:36

      I was able to download and build the workspace. Just FYI…the icons in your project are hard-pathed to a directory beneath E:\Pictures. This isn’t a huge deal since I was able to pull from another icon library and just rename the files to match yours.

      This is the only working object browser that I’ve been able to find (the Code Project application is built around file objects only and it won’t compile without a lot of clean-up). Now if I can just figure out the permission issue that prevents access to the WinSta0 Window Station I’ll be good, but that’s my problem to figure out.

      Thanks again!

      Reply
      1. Shane BurkeJune 2, 2016 ב 17:49

        Scratch that last blurb in my previous comment. I see now that the Window Station issue isn’t permission related. Rather, there is currently no support for browsing Windows Station objects.

        Reply