HandleDump example

This example shows how to use DbgHelp functions (MiniDumpWriteDump, MiniDumpCallback, MiniDumpReadDumpStream) to get the list of handles opened by a process.

Background

With the help of MiniDumpWriteDump function, we can create a minidump with handle information. With the help of MiniDumpReadDumpStream function, we can read the contents of the minidump, including handle information. If we sum up these two simple facts, in the outcome we get the possibility to obtain the list of handles opened by a process, without the need to resort to undocumented functions NtQuerySystemInformation and NtQueryObject. HandleDump example shows how to do it.

The process of getting the list of handles consists of the following steps:

  • Create a minidump of the target process (with handle information included)
  • Read the handle information from the minidump

Both steps are described below in more details

Creating the minidump

In order to create the minidump, we have to have a handle to the target process. If we know the id of the process, we can use OpenProcess function to get the handle. Since full access to the target process is required to create the minidump successfully, it is useful to enable the debug privilege before calling OpenProcess.

After that, we are ready to create the minidump. Since we are only interested in handle information, we pass MiniDumpWithHandleData flag to MiniDumpWriteDump function. In order to keep the minidump as small as possible, MiniDumpCallback function is also used – it handles IncludeModuleCallback and IncludeThreadCallback callbacks and excludes all module and thread information from the minidump. (In this article you can find more information about MiniDumpCallback function and the ways to control the size and contents of the minidump).

Reading handle information from the minidump

After we have created the minidump, it’s time to read it and extract the list of handles opened by the process. At the beginning, we have to map the minidump file into memory using CreateFileMapping and MapViewOfFile functions. Then we use MiniDumpReadDumpStream function to get the contents of the stream that contains the handle information. The stream starts with the following header structure:

typedef struct _MINIDUMP_HANDLE_DATA_STREAM {
    ULONG32 SizeOfHeader;        // sizeof(MINIDUMP_HANDLE_DATA_STREAM)
    ULONG32 SizeOfDescriptor;    // sizeof(MINIDUMP_HANDLE_DESCRIPTOR)
    ULONG32 NumberOfDescriptors;
    ULONG32 Reserved;
} MINIDUMP_HANDLE_DATA_STREAM;

The first two members allow us to verify that the stream format is valid and can be understood by our application. The third member (NumberOfDescriptors) contains the number of MINIDUMP_HANDLE_DESCRIPTOR structures that follow the header. Every MINIDUMP_HANDLE_DESCRIPTOR structure describes a handle in the handle table of the target process:

typedef struct _MINIDUMP_HANDLE_DESCRIPTOR {
    ULONG64  Handle;
    RVA      TypeNameRva;
    RVA      ObjectNameRva;
    ULONG32  Attributes;
    ULONG32  GrantedAccess;
    ULONG32  HandleCount;
    ULONG32  PointerCount;
} MINIDUMP_HANDLE_DESCRIPTOR;

Most of the members of this structure have an obvious meaning, but TypeNameRva and ObjectNameRva might need a short explanation. They contain an RVA (relative virtual address) from the beginning of the minidump data in memory (as returned by MapViewOfFile) to the location where a MINIDUMP_STRING structure is stored. This structure is defined as the following:

typedef struct _MINIDUMP_STRING {
    ULONG32 Length;         // Size of the buffer, in bytes
    WCHAR   Buffer [0];     // The buffer with the string (null-terminated)
} MINIDUMP_STRING;

When we know the format of all structures used to describe handle information, reading it from the minidump is easy.

Limitations

While the handle information collected by MiniDumpWriteDump is undoubtedly useful, it has a serious limitation – it does not contain the names of open files (that is, object name is always empty for handles that represent open files). At the same time, object names are available for handles that represent other types of kernel objects.

Supported operating systems

Windows NT 4.0, Windows 2000, Windows XP and Windows Server 2003.

Sample files

The sample application consists of three source files. GetHandles.h and GetHandles.cpp contain the implementation of CGetHandles class, which hides the details of obtaining the handle information behind an easy-to-use interface. HandleDump.cpp file uses this interface to read the handle information and display it to the user in readable format.

Download

Download HandleDump source code (6 KB)
Download HandleDump executable (no source code) (440 KB)

Build instructions

The following steps are needed to build the example:

1. Find the latest versions of DbgHelp.dll, DbgHelp.h and DbgHelp.lib files.

The latest versions of these files are always available with Debugging Tools for Windows package. Download and install Debugging Tools for Windows (when installing, choose custom installation and install SDK).

2. Configure Visual Studio to find DbgHelp.h and DbgHelp.lib files.

These files are supplied with Visual Studio and Platform SDK, but it is necessary to use the latest files – the files that come with Debugging Tools for Windows. Thus it is necessary to configure Visual Studio include and library directories so that the latest files will be found first.

Use the following directories:
Include -> %DebuggingTools%\sdk\inc
Library -> %DebuggingTools%\sdk\lib\i386

3. Create an empty console project and add the source files to the project.

4. When the project is built, make sure that the executable can find the latest version of DbgHelp.dll.

Copy DbgHelp.dll from the installation directory of Debugging Tools for Windows to the directory where the example executable resides.