The Windows Processes and Threads 14

 

 

 

 

 

Terminating a Process

 

Terminating a process has the following results:

 

  1. Any remaining threads in the process are marked for termination.
  2. Any resources allocated by the process are freed.
  3. All kernel objects are closed.
  4. The process code is removed from memory.
  5. The process exit code is set.
  6. The process object is signaled.

 

While open handles to kernel objects are closed automatically when a process terminates, the objects themselves exist until all open handles to them are closed. Therefore, an object will remain valid after a process that is using it terminates if another process has an open handle to it. The GetExitCodeProcess() function returns the termination status of a process. While a process is executing, its termination status is STILL_ACTIVE. When a process terminates, its termination status changes from STILL_ACTIVE to the exit code of the process.

When a process terminates, the state of the process object becomes signaled, releasing any threads that had been waiting for the process to terminate. When the system is terminating a process, it does not terminate any child processes that the process has created. Terminating a process does not generate notifications for WH_CBT hook procedures. Use the SetProcessShutdownParameters() function to specify certain aspects of the process termination at system shutdown, such as when a process should terminate relative to the other processes in the system.

 

How Processes are Terminated

 

A process executes until one of the following events occurs:

 

  1. Any thread of the process calls the ExitProcess() function. Note that some implementation of the C run-time library (CRT) call ExitProcess if the primary thread of the process returns.
  2. The last thread of the process terminates.
  3. Any thread calls the TerminateProcess() function with a handle to the process.
  4. For console processes, the default console control handler calls ExitProcess() when the console receives a CTRL+C or CTRL+BREAK signal.
  5. The user shuts down the system or logs off.

 

Do not terminate a process unless its threads are in known states. If a thread is waiting on a kernel object, it will not be terminated until the wait has completed. This can cause the application to hang. The primary thread can avoid terminating other threads by directing them to call ExitThread() before causing the process to terminate. The primary thread can still call ExitProcess() afterwards to ensure that all threads are terminated. The exit code for a process is either the value specified in the call to ExitProcess() or TerminateProcess(), or the value returned by the main or WinMain() function of the process. If a process is terminated due to a fatal exception, the exit code is the value of the exception that caused the termination. In addition, this value is used as the exit code for all the threads that were executing when the exception occurred.

If a process is terminated by ExitProcess(), the system calls the entry-point function of each attached DLL with a value indicating that the process is detaching from the DLL. DLLs are not notified when a process is terminated by TerminateProcess(). If a process is terminated by TerminateProcess(), all threads of the process are terminated immediately with no chance to run additional code. This means that the thread does not execute code in termination handler blocks. In addition, no attached DLLs are notified that the process is detaching. If you need to have one process terminate another process, the following steps provide a better solution:

 

  1. Have both processes call the RegisterWindowMessage() function to create a private message.
  2. One process can terminate the other process by broadcasting a private message using the BroadcastSystemMessage() function as follows:

 

      DWORD dwRecipients = BSM_APPLICATIONS;

      UINT uMessage = PM_MYMSG;

      WPARAM wParam = 0;

      LPARAM lParam = 0;

 

      BroadcastSystemMessage(

            BSF_IGNORECURRENTTASK, // do not send message to this process

            &dwRecipients,         // broadcast only to applications

            uMessage,                 // registered private message

            wParam,                     // message-specific value

            lParam );                     // message-specific value

 

  1. The process receiving the private message calls ExitProcess() to terminate its execution.

 

The execution of the ExitProcess(), ExitThread(), CreateThread(), CreateRemoteThread(), and CreateProcess() functions is serialized within an address space. The following restrictions apply:

 

  1. During process startup and DLL initialization routines, new threads can be created, but they do not begin execution until DLL initialization is finished for the process.
  2. Only one thread at a time can be in a DLL initialization or detach routine.
  3. The ExitProcess() function does not return until there are no threads are in their DLL initialization or detach routines.

 

Process Working Set

 

The working set of a program is a collection of those pages in its virtual address space that have been recently referenced. It includes both shared and private data. The shared data includes pages that contain all instructions your application executes, including those in your DLLs and the system DLLs. As the working set size increases, memory demand increases. A process has an associated minimum working set size and maximum working set size. Each time you call CreateProcess(), it reserves the minimum working set size for the process. The virtual memory manager attempts to keep enough memory for the minimum working set resident when the process is active, but keeps no more than the maximum size.

To get the requested minimum and maximum sizes of the working set for your application, call the GetProcessWorkingSetSize() function. The system sets the default working set sizes. You can also modify the working set sizes using the SetProcessWorkingSetSize() function. Setting these values is not a guarantee that the memory will be reserved or resident. Be careful about requesting too large a minimum or maximum working set size, because doing so can degrade system performance. To obtain the current or peak size of the working set for your process, use the GetProcessMemoryInfo() function.

 

Process Security and Access Rights

 

The Microsoft Windows security model enables you to control access to process objects. When a user logs in, the system collects a set of data that uniquely identifies the user during the authentication process, and stores it in an access token. This access token describes the security context of all processes associated with the user. The security context of a process is the set of credentials given to the process or the user account that created the process. You can use a token to specify the current security context for a process using the CreateProcessWithTokenW() function. You can specify a security descriptor for a process when you call the CreateProcess(), CreateProcessAsUser(), or CreateProcessWithLogonW() function. If you specify NULL, the process gets a default security descriptor. The ACLs in the default security descriptor for a process come from the primary or impersonation token of the creator. To retrieve a process's security descriptor, call the GetSecurityInfo() function. To change a process's security descriptor, call the SetSecurityInfo() function. The valid access rights for process objects include the standard access rights and some process-specific access rights. The following table lists the standard access rights used by all objects.

 

String Constant (Value)

Meaning

DELETE (0x00010000L)

Required to delete the object.

READ_CONTROL (0x00020000L)

Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right.

SYNCHRONIZE (0x00100000L)

The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.

WRITE_DAC (0x00040000L)

Required to modify the DACL in the security descriptor for the object.

WRITE_OWNER (0x00080000L)

Required to change the owner in the security descriptor for the object.

 

The following table lists the process-specific access rights.

 

String Constant (Value)

Meaning

PROCESS_ALL_ACCESS

All possible access rights for a process object. For Windows Server 2003 and Windows XP/2000:  The size of the PROCESS_ALL_ACCESS flag increased on Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003 or Windows XP/2000, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this problem, specify the minimum set of access rights required for the operation. If PROCESS_ALL_ACCESS must be used, set _WIN32_WINNT to the minimum operating system targeted by your application (for example, #define _WIN32_WINNT _WIN32_WINNT_WINXP).

PROCESS_CREATE_PROCESS (0x0080)

Required to create a process.

PROCESS_CREATE_THREAD (0x0002)

Required to create a thread.

PROCESS_DUP_HANDLE (0x0040)

Required to duplicate a handle using DuplicateHandle().

PROCESS_QUERY_INFORMATION (0x0400)

Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken(), GetExitCodeProcess(), GetPriorityClass(), and IsProcessInJob()).

PROCESS_QUERY_LIMITED_INFORMATION (0x1000)

Required to retrieve certain information about a process. A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. For Windows Server 2003 and Windows XP/2000:  This access right is not supported.

PROCESS_SET_INFORMATION (0x0200)

Required to set certain information about a process, such as its priority class.

PROCESS_SET_QUOTA (0x0100)

Required to set memory limits using SetProcessWorkingSetSize().

PROCESS_SUSPEND_RESUME (0x0800)

Required to suspend or resume a process.

PROCESS_TERMINATE (0x0001)

Required to terminate a process using TerminateProcess().

PROCESS_VM_OPERATION (0x0008)

Required to perform an operation on the address space of a process.

PROCESS_VM_READ (0x0010)

Required to read memory in a process using ReadProcessMemory().

PROCESS_VM_WRITE (0x0020)

Required to write to memory in a process using WriteProcessMemory().

SYNCHRONIZE (0x00100000L)

Required to wait for the process to terminate using the wait functions.

 

To open a handle to another process and obtain full access rights, you must enable the SeDebugPrivilege privilege. The handle returned by the CreateProcess() function has PROCESS_ALL_ACCESS access to the process object. When you call the OpenProcess() function, the system checks the requested access rights against the DACL in the process's security descriptor. When you call the GetCurrentProcess() function, the system returns a pseudohandle with the maximum access that the DACL allows to the caller.

You can request the ACCESS_SYSTEM_SECURITY access right to a process object if you want to read or write the object's SACL. Warning:  A process that has some of the access rights noted here can use them to gain other access rights. For example, if process A has a handle to process B with PROCESS_DUP_HANDLE access, it can duplicate the pseudo handle for process B. This creates a handle that has maximum access to process B.

 

 

 

< Processes & Threads 13 | Win32 Process & Thread Programming | Win32 Programming | Processes & Threads 15 >