Kim Gräsman, September 2003
Astute readers have noted that MiniDumpWriteDump does not actually require the SeDebugPrivilege. There's something about MiniDumpWriteDump that requires some permission or privilege, but it's not «Debug Programs».
However, enabling «Debug Programs» bypasses enough access checks to always allow the dump operation, so it works around the problem, but in doing so also allows your process to break into any other process on the system, which is a terrible idea from a security standpoint.
I suspect MiniDumpWriteDump needs some permission on the process object in question, but I have yet to figure out what it is. If I do find out, I'll amend the article with proper advice.
- Kim
This is just a brief text aimed at supplementing other, better articles on the MiniDump API. For good introductions, see;
| Andy Pennell — Post-Mortem Debugging Your Application... |
| John Robbins — Bugslayer from 2002-06 |
| Matt Pietrek — Under The Hood from 2002-03 |
I might extend this as I discover new issues or features and, more importantly, as I find more time for writing.
MiniDumpWriteDump is one of the coolest additions to the new DBGHelp API (vers. 4.0.18.0 & 6.0.17.0). However, there are a couple of things about it that are not entirely obvious;
The thread-safety bit is quite easily handled with a process-global synchronization primitive. The privilege problem, however, may be harder for the uninitiated to solve, and it's not documented, so it may come as a surprise — it sure did to me.
Since DBGHELP.dll is not inherently thread-safe, making calls into it from more than one thread simultaneously may yield undefined behavior (whatever that means). So, if the client using the MiniDump API has multiple threads, or is called by multiple threads in a non-synchronized manner, you must make sure that all calls into DBGHELP.dll are isolated.
The provided sample code solves this by taking a pointer to an existing, initialized critical section, which is acquired by CMiniDump::Create() prior to calling MiniDumpWriteDump.
// Calling code
#include "MiniDump.h"
CRITICAL_SECTION g_CS;
unsigned long ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers)
{
CMiniDump::Create(NULL, pExceptionPointers, &g_CS);
return EXCEPTION_EXECUTE_HANDLER;
}
Of course, the actual CMiniDump class could contain a static CRITICAL_SECTION member, but the DBGHelp API isn't about MiniDumps alone.
For example, what would happen if another thread called John Robbins' Stackwalk API while our thread was executing CMiniDump::Create()? It's pretty hard to tell without seeing the DBGHelp source code, and any possible interdependencies between MiniDumpWriteDump and the Sym-family of functions. If they were to share any state, access to that data would not be synchronized and all sorts of race conditions could occur.
With this in mind, it seems like a good idea to have a shared critical section to use for all access to DBGHELP.dll, and this is why the Create() method takes it as a parameter, allowing multiple classes using DBGHelp to share the same CRITICAL_SECTION instance.
Since a MiniDump contains a lot of metadata about the OS and application state at the time of the dump, it is a rather privileged operation. The resulting dump could potentially contain passwords, credit card numbers, and other sensitive information, and I assume this is the reason MiniDumpWriteDump requires the "Debug Programs" privilege in order to execute.
"Debug Programs" (whose non-localized name is "SeDebugPrivilege") not surprisingly denotes the right to debug programs, i.e. right-clicking them in Task Manager and selecting Debug, or firing up WinDBG and choosing Attach to Process.
This is all fine, but the fact that it isn't mentioned on MSDN is questionable, to say the least. Most desktop apps will never notice, since many users have administrative privileges on their machines, but if MiniDumpWriteDump is to be called from a web application running under a locked-down user account, it just won't work. Besides, according to the principle of least privilege (Item #7), this is a pretty bad requirement for any application.
Making sure that the "Debug Programs" privilege is enabled is, unfortunately, less than trivial. Keith Brown's Programming Windows Security gave me the following insights;
This forms a kind of hierarchy, where the user account serves as a template for a logon session, which in turn serves as a template for tokens. Unfortunately there is no way to just inject a privilege into a token if it's not present in the logon session and thereby in the user account. So, the first necessary step is to enable the privilege on the user account. The image below shows the administration GUI available for this; an MMC snap-in called Local Security Policy (available under Start->Programs->Administrative Tools).

As previously discussed, the MiniDumpWriteDump function requires the "Debug Programs" privilege to execute, and the image shows how to enable it. Press the Add... button, select the user, and check the Local Policy Setting box (this image is from my old Windows 2000 installation, and apparently the GUI changed for XP. You'll figure it out.) Note that in order for the privilege to become effective, the user's logon session needs to be re-constructed. For an interactive user, this means logging off and back on, and for a service it means a restart.
Granting a user account the Debug Programs privilege is not exactly common-place, since it allows the account to break into processes as a debugger, scan their memory, etc. This clearly poses a security risk - if somebody is able to exploit our application to execute arbitrary code, they can perform virtually any operation. Fortunately, from a security perspective, this privilege is disabled by default (on Windows XP, this may vary between OS versions).
Now that the user account, logon sessions and tokens all contain the privilege, albeit disabled, we want to temporarily enable it, just before writing the dump and then re-disable it as soon as we're done. This leaves the "privileged window" - where bad guys would want to mess with our code - at a very modest opening. In code, this reads:
CSmartHandle hImpersonationToken = NULL;
if(!GetImpersonationToken(&hImpersonationToken.m_h))
{
return FALSE;
}
// ...
// We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
TOKEN_PRIVILEGES tp;
BOOL bPrivilegeEnabled = EnablePriv(SE_DEBUG_NAME, hImpersonationToken, &tp);
// DBGHELP.DLL is not thread safe
EnterCriticalSection(pCS);
bRet = pDumpFunction(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &stInfo, NULL, NULL);
LeaveCriticalSection(pCS);
if(bPrivilegeEnabled)
{
// Restore the privilege
RestorePriv(hImpersonationToken, &tp);
}
The sample code shows the actual implementations of EnablePrivilege and RestorePrivilege (which are essentially thin wrappers around AdjustTokenPrivileges), as well as GetImpersonationToken. The latter is a workaround for the fact that the current thread may not have a token assigned to it, and if not, the process token is received.
One may argue that it's equally simple for an attacker to enable/disable privileges if they manage to inject custom code into our process. So, consider the possible effects of requiring the Debug Programs privilege before settling on this design.
This article has shown how to use the MiniDump API in a multithreaded, least-privilege environment. As of right now, documentation in MSDN is rather new on the fact that DBGHELP.DLL is not thread-safe, and doesn't yet mention that SeDebugPrivilege is required, so this is intended to complement available texts.
The provided sample code is a kind of general purpose best-practice for using the MiniDumpWriteDump API. Parts of it can be used to complement existing implementations, such as John Robbins' BugslayerUtil.dll or Andy Pennell's sample code.
I've tested the provided code briefly, but that's about it. Consider it a modest overview of what is needed to make the MiniDump API work in a multithreaded, restricted-privilege environment.
The zip contains the CMiniDump class, a simple test app, and projects for Visual Studio 6.0 and Visual Studio .NET 2003.
| minidump-sample.zip | Minidump sample |
If you find the code is broken or inadequate, or worse, the article leaves something to desire, please contact me.