Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > March 1998
March 1998


Code for this article: W32Mar98.exe (5KB)
Jeffrey Richter wrote Advanced Windows, Third Edition (Microsoft Press, 1998) and Windows 95: A Developer's Guide (M&T Books, 1995). Jeff is a consultant and teaches Win32 programming courses (www.solsem.com). He can be reached at www.JeffreyRichter.com.

Q I am working on developing a Windows NT®-based service. My program has bugs just like any program. When I have a bug in my service, I sometimes want to just kill the service and restart it. I frequently use the Task Manager to kill a process that I don't want running any more. But, if I try to use the Task Manager to kill a service, I get a message box. Is there any way to force Task Manager to kill a service?
Andrea DeMaio
Pacific Grove, CA

A Before I answer this question, let's take a close look at what is going on here and explain why Task Manager is displaying the message box (see Figure 1). When you select a process in Task Manager and click the End Process button, Task Manager grabs the ID of the chosen process and executes the following pseudocode:


 DWORD dwProcessId = (Initialized from the ListView
                      control);
 HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE,
                               dwProcessId);
 if (hProcess == NULL) {
    // Display error message box (like in Figure 1).
 } else {
    TerminateProcess(hProcess, 1);
    CloseHandle(hProcess);
 }
The message box is displayed because OpenProcess can't open a handle to the specified process. This fails because the service is running under the Local System security account and you, even when logged on as an administrator, do not have sufficient access to open a handle to the service process. In fact, Task Manager calls GetLastError immediately after OpenProcess returns NULL and receives an error code of ERROR_ACCESS_DENIED (5). Task Manager places text representing this error in the message box.
Figure 1 Error Message Box
Figure 1 Error Message Box


Microsoft allows a process to override this access check by using a privilege. In Windows NT, a privilege is an attribute assigned to a user that allows the user to override what would normally be a restriction to some part of the operating system. Windows NT 4.0 supports about 24 privileges that allow users to do things like backup and restore files that they wouldn't normally be allowed to access, change the system time, run a process in the realtime priority class, and shut down the system.
One of these privileges is called SeDebugPrivilege. Normally, the operating system forbids users from debugging processes. This makes sense, of course, because you don't want to allow users (or hackers) the ability to alter a process while it is executing. However, there is a class of users who do need to be able to debug processes. By default, Windows NT automatically assigns the debug privilege to administrators. You can verify this by running the User Manager administrative tool, selecting the Policies | User Rights menu option, clicking the Show Advanced User Rights checkbox, and then choosing the Debug programs right (see Figure 2).

Figure 2 User Rights Policy
Figure 2 User Rights Policy


If you are granted the debug programs privilege when you log on, you're no longer restricted from debugging applications. What does this mean? Well, to debug an application you must be allowed to call OpenProcess successfully so that you can call various debugger-related APIs like ReadProcessMemory, WriteProcessMemory, VirtualQueryEx, VirtualProtectEx, VirtualAllocEx, VirtualFreeEx, CreateRemoteThread, and so on. All of these functions require a handle to a process to work correctly. Simply stated, the debug privilege forces OpenProcess to open the process even if you'd normally be restricted from doing so.
After reading all of this, you should still be confused. You're probably saying to yourself: "Hey, I am logged on as an administrator when I tell Task Manager to end the service process and I still get the access denied message box. Why?" Task Manager still doesn't let you end the process by design. Holding a privilege overrides the normal behavior and allows unrestricted access to very powerful OS capabilities. The system is trying to protect the average user. You could easily imagine a scenario where an administrator logs onto a machine and then tells Task Manager to kill WinLogon.exe or CSRSS.exe processes. These processes absolutely must be up and running all the time or you will crash your entire Windows NT system. Because these processes run as services, Task Manager won't let an administrator accidentally terminate them, bringing down the entire system.
There are three relationships a user can have with a privilege: denied, granted, and enabled. "Denied" means the user never has the ability to override their restricted access to a component of the system. For example, you saw that guests were denied the debug privilege; therefore, a guest can never debug a process. Most privileges are denied to most users of the system. An administrator can grant a privilege to a user using the User Manager tool. Keep in mind that the user must log off (if currently logged on) and log back on for the newly assigned privilege to be granted and take effect.
"Granted" means that the user can run a program that takes advantage of the privilege. This does not mean what most people think it means. Remember that privileges give a user access to a normally restricted and powerful operation. This is potentially a very dangerous thing and should not be entered into lightly. So, even though a user may be granted a privilege, the system doesn't actually take the privilege into account until it has been explicitly enabled.
"Enabled" means that the user has been granted the privilege and that the privilege has been turned on (so to speak). Now, when you perform a restricted operation, the system checks to see which privileges are enabled (not just granted) and then overrides the restriction, if appropriate. This explains why Task Manager doesn't let administrators terminate a service. Even though administrators have been granted the debug privilege, Task Manager hasn't enabled it.
When you log on to the system, Windows NT creates an object called an access token. This object contains a lot of information, including which privileges have been granted or enabled. The access token is then associated with Explorer.exe, the shell process. Whenever you run a new process, the system makes a copy of the parent process's access token and associates it with the new process. In other words, each process has its own set of granted and enabled privileges. This also means that if you enable a privilege for one process, that privilege is not enabled for any other process unless you explicitly enable it for that other process as well. So, if you enable the debug privilege for the Task Manager process, Task Manager will be able to terminate service processes.
Figure 3 shows a small program, EnableDebugPrivAndRun, that demonstrates how to run a process with the debug privilege enabled. To run Task Manager with its debug privilege enabled, build the program and run it using a command line like this:


 C:\>EnableDebugPrivAndRun.exe TaskMgr.exe
When you execute this command line, EnableDebugPrivAndRun first calls OpenProcessToken. This returns a handle to the access token currently associated with EnableDebugPrivAndRun's process. If I get a handle to the access token successfully, I call EnablePrivilege—my own function that simply packages up all the steps necessary to enable or disable a privilege.
EnablePrivilege requires three parameters: the handle of the access token containing the user's privilege information, a string name that identifies the privilege that you want to alter, and a Boolean indicating whether you want to enable or disable this privilege. With these three parameters, EnablePrivilege initializes a TOKEN_PRIVILEGES structure. It's actually a variable-length structure because it allows you to specify several privileges at once. Since my EnablePrivilege function allows you to change only one privilege at a time, the code is much simpler than what it would have to be to alter two or more privileges at once.
The LookupPrivilegeValue function converts the privilege string name into a 64-bit numeric equivalent called a LUID (locally unique identifier). This will seem a bit strange to most developers. Usually, developers like to work with numbers and avoid strings; but when working with privileges, programs always start out by referring to them by their programmatic string names. You then convert this string name to a number by calling LookupPrivilegeValue. LUIDs, by the way, are numbers that are guaranteed to be unique on the local system. You cannot pass a LUID between machines and, in fact, you cannot hold on to a LUID across a reboot on the same machine. In other words, passing the debug privilege string to LookupPrivilegeValue may cause two different LUIDs to be returned if you reboot the machine between calls.
Once you have the current LUID for the debug privilege, EnablePrivilege calls AdjustTokenPrivileges to either enable or disable the privilege. You'll notice that when I call OpenProcessToken, I request TOKEN_ADJUST_PRIVILEGES access; this is required to call AdjustTokenPrivileges successfully.
After EnablePrivilege returns, I pass EnableDebugPrivAndRun's command line off to the ShellExecute function. ShellExecute will call CreateProcess internally to spawn the new process. Remember that access tokens are inherited, so this new process will inherit EnableDebugPrivAndRun's access token, which now has the debug privilege enabled. This means the new process will also have the debug privilege enabled and will be able to call OpenProcess successfully. By the way, this change will also allow you to use Task Manager to change the priority class and the processor affinity of a service. Bonus features!
I like to have the Task Manager running on my system so that I can always see my CPU's performance by looking at the little icon just to the left of the clock on my taskbar. Here's what I do to make sure that Task Manager is always running with the debug privilege enabled. First, I build my EnableDebugPrivAndRun program and place it in a directory. Then I create a shortcut that runs the Task Manager with its window initially minimized. I place this shortcut's .LNK file in the same directory as the EnableDebugPrivAndRun program. I create another shortcut that runs EnableDebugPrivAndRun, passing it the Task Manager shortcut's file name as an argument. The shortcut's command-line string looks like this:

 C:\Bin\EnableDebugPrivAndRun.exe "C:\Bin\Task Manager.lnk"
I place this second shortcut in my Start Menu's Startup folder so that the system invokes it as soon as I log on. And there you have it.

Q In your book, Advanced Windows (3rd edition, page 127), you mention that when Windows 95 or Windows NT runs an executable image from a floppy disk, the system actually loads the entire file's image into RAM (and pages it to the system's paging file if necessary). The system does this so that the diskette can be removed from the drive without affecting the application.
This sounds like a great idea and I'm glad that Microsoft added this feature to the operating system. I can think of many more occasions where this would be really useful. For example, I'd like the system to load my image file into RAM if the file is located on a network drive or on a CD-ROM drive. I might also want to load the image into RAM if the file is compressed or encrypted (for Windows NT 5.0). Is there any way that I can force the system to load certain file images into RAM?

Frank Merrow

A Starting with Windows NT 4.0, Microsoft added support for this in the loader. When you build an executable image (EXE or DLL), you can specify the following switch to the linker:


 /SWAPRUN:NET
This switch tells the linker to turn on a certain flag in the resulting image file. When the loader loads your image file, the loader checks to see if this bit is turned on and if the image is being loaded from a network drive. If so, the loader forces the file's image into RAM in order to prevent paging across the network.
The linker also supports this form of the SWAPRUN switch:

 /SWAPRUN:CD
This switch tells the loader to force the image into RAM if the file image is running from a CD-ROM drive. Note that for these switches to work, your linker must support the switch and the loader must recognize the switch and do the right thing. Windows NT 4.0 and later recognizes the /SWAPRUN:NET and /SWAPRUN:CD bits in the executable file image; Windows® 95 does not.
Currently, the loader does not support forcing compressed or encrypted image files into RAM. But it is very easy to write a function that forces an entire image file into RAM. Figure 4 shows just such a function, RunImageLocally. This function takes a single parameter, the HINSTANCE (or HMODULE) of an executable image. This parameter is either the value passed to your WinMain function, the value passed to your DllMain function, or the value returned from a call to LoadLibrary. The function then calls VirtualQuery in a loop cycling through all of the memory regions contained inside the executable image (that is, regions that all share the same allocation base).
For each region, I check to see if the pages are committed and are not guard pages. If the pages aren't committed, there is no storage there to force into RAM. If the pages have the PAGE_GUARD attribute, then touching them would generate a STATUS_GUARD_PAGE_VIOLATION exception, which the application is presumably waiting to catch with an exception handler. I don't want to confuse the application, so I can't force these pages into RAM either.
For all other pages, I check to see if they are writeable. If they aren't, then I must change the protection on the pages so that I can write to them. I use PAGE_EXECUTE_READWRITE because this is the most liberal of all protections. Once I know that the pages are writeable, I then write to a single DWORD on each page in the region. Performing this write causes the system to make a copy of the page in RAM (later to be swapped to the paging file if necessary). After all of the pages in the region have been written to, I change the page protections back to what they originally were, if necessary. Finally, I repeat this for any additional regions contained inside the mapped executable image file.
It should be noted that this procedure is not thread-safe and there is really no way to make it thread-safe. It is possible that other threads in the process can be changing the protections of the file image's pages executing code or touching data within these pages. Executing code or touching data should not be a problem at all. But if another thread is changing the page protections, this could be a potential problem. Realistically, however, this is not something that I would worry about.
I tested this code only on Windows NT, but it should work just fine on Windows 95. Also, there is less reason to use this technique on Windows 95 because it does not support the copy-on-write mechanism. When you load an image into Windows 95, the loader automatically and immediately allocates RAM (and paging file space) for all the writeable pages in the image file. Only read-only pages would be swapped between RAM and the original file.

Have a question about programming in Win32?
Send your questions via email to Jeffrey Richter from his website at http://www.jeffreyrichter.com.

From the March 1998 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1998 Microsoft Corporation. All rights reserved.
Terms of Use
.

© 2014 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft