Object Manager

 < Day Day Up > 

As mentioned in Chapter 2, Windows implements an object model to provide consistent and secure access to the various internal services implemented in the executive. This section describes the Windows object manager, the executive component responsible for creating, deleting, protecting, and tracking objects. The object manager centralizes resource control operations that otherwise would be scattered throughout the operating system. It was designed to meet the goals listed on later in the chapter.

EXPERIMENT: Exploring the Object Manager

Throughout this section, you'll find experiments that show you how to peer into the object manager database. These experiments use the following tools, which you should become familiar with if you aren't already:

  • Winobj (available from http://www.sysinternals.com) displays the internal object manager's namespace. There is also a version of Winobj in the Platform SDK (in \Program Files\Microsoft Platform SDK\Bin\Winnt\Winobj.exe), but the Winobj from http://www.sysinternals.com displays more accurate information about objects (such as the reference count, the number of open handles, security descriptors, and so forth).

  • Process Explorer and Handle from http://www.sysinternals.com (introduced in Chapter 1) displays the open handles for a process.

  • Oh.exe (available in Windows resource kits) displays the open handles for a process, but it requires a global flag to be set in order to operate.

  • The Openfiles /query command (in Windows XP and Windows Server 2003) displays the open handles for a process, but it requires a global flag to be set in order to operate.

  • The kernel debugger !handle command displays the open handles for a process.

The object viewer provides a way to traverse the namespace that the object manager maintains. (As we'll explain later, not all objects have names.) Try running the WinObj object manager utility from http://www.sysinternals.com and examining the layout, shown here:



As noted previously, both the OH utility and the Openfiles /query command require that a Windows global flag called maintain objects list be enabled. (See the "Windows Global Flags" section later in this chapter for more details about global flags.) OH will set the flag if it is not set. If you type Openfiles /Local, it will tell you whether the flag is enabled. You can enable it with the Openfiles /Local ON command. In either case, you must reboot the system for the setting to take effect. Neither Process Explorer nor Handle from http://www.sysinternals.com require object tracking to be turned on because they use a device driver to obtain the information.


The object manager was designed to meet the following goals:

  • Provide a common, uniform mechanism for using system resources

  • Isolate object protection to one location in the operating system so that C2 security compliance can be achieved

  • Provide a mechanism to charge processes for their use of objects so that limits can be placed on the usage of system resources

  • Establish an object-naming scheme that can readily incorporate existing objects, such as the devices, files, and directories of a file system, or other independent collections of objects

  • Support the requirements of various operating system environments, such as the ability of a process to inherit resources from a parent process (needed by Windows and POSIX) and the ability to create case-sensitive filenames (needed by POSIX)

  • Establish uniform rules for object retention (that is, for keeping an object available until all processes have finished using it)

Internally, Windows has two kinds of objects: executive objects and kernel objects. Executive objects are objects implemented by various components of the executive (such as the process manager, memory manager, I/O subsystem, and so on). Kernel objects are a more primitive set of objects implemented by the Windows kernel. These objects are not visible to user-mode code but are created and used only within the executive. Kernel objects provide fundamental capabilities, such as synchronization, on which executive objects are built. Thus, many executive objects contain (encapsulate) one or more kernel objects, as shown in Figure 3-17.

Figure 3-17. Executive objects that contain kernel objects


Details about the structure of kernel objects and how they are used to implement synchronization are given later in this chapter. In the remainder of this section, we'll focus on how the object manager works and on the structure of executive objects, handles, and handle tables. Here we'll just briefly describe how objects are involved in implementing Windows security access checking; we'll cover this topic thoroughly in Chapter 8.

Executive Objects

Each Windows environment subsystem projects to its applications a different image of the operating system. The executive objects and object services are primitives that the environment subsystems use to construct their own versions of objects and other resources.

Executive objects are typically created either by an environment subsystem on behalf of a user application or by various components of the operating system as part of their normal operation. For example, to create a file, a Windows application calls the Windows CreateFile function, implemented in the Windows subsystem DLL Kernel32.dll. After some validation and initialization, CreateFile in turn calls the native Windows service NtCreateFile to create an executive file object.

The set of objects an environment subsystem supplies to its applications might be larger or smaller than the set the executive provides. The Windows subsystem uses executive objects to export its own set of objects, many of which correspond directly to executive objects. For example, the Windows mutexes and semaphores are directly based on executive objects (which are in turn based on corresponding kernel objects). In addition, the Windows subsystem supplies named pipes and mailslots, resources that are based on executive file objects. Some subsystems, such as POSIX, don't support objects as objects at all. The POSIX subsystem uses executive objects and services as the basis for presenting POSIX-style processes, pipes, and other resources to its applications.

Table 3-3 lists the primary objects the executive provides and briefly describes what they represent. You can find further details on executive objects in the chapters that describe the related executive components (or in the case of executive objects directly exported to Windows, in the Windows API reference documentation).

Table 3-3. Executive Objects Exposed to the Windows API

Object Type

Represents

Symbolic link

A mechanism for referring to an object name indirectly.

Process

The virtual address space and control information necessary for the execution of a set of thread objects.

Thread

An executable entity within a process.

Job

A collection of processes manageable as a single entity through the job.

Section

A region of shared memory (known as a file mapping object in Windows).

File

An instance of an opened file or an I/O device.

Access token

The security profile (security ID, user rights, and so on) of a process or a thread.

Event

An object with a persistent state (signaled or not signaled) that can be used for synchronization or notification.

Semaphore

A counter that provides a resource gate by allowing some maximum number of threads to access the resources protected by the semaphore.

Mutex*

A synchronization mechanism used to serialize access to a resource.

Timer

A mechanism to notify a thread when a fixed period of time elapses.

IoCompletion

A method for threads to enqueue and dequeue notifications of the completion of I/O operations (known as an I/O completion port in the Windows API).

Key

A mechanism to refer to data in the registry. Although keys appear in the object manager namespace, they are managed by the configuration manager, in a way similar to that in which file objects are managed by file system drivers. Zero or more key values are associated with a key object; key values contain data about the key.

WindowStation

An object that contains a clipboard, a set of global atoms, and a group of desktop objects.

Desktop

An object contained within a window station. A desktop has a logical display surface and contains windows, menus, and hooks.


Note

The executive implements a total of 27 object types in Windows 2000 and 29 on Windows XP and Windows Server 2003. (These newer Windows versions add the DebugObject and KeyedEvent objects.) Many of these objects are for use only by the executive component that defines them and are not directly accessible by Windows APIs. Examples of these objects include Driver, Device, and EventPair.


Note

Externally in the Windows API, mutants are called mutexes. Internally, the kernel object that underlies mutexes is called a mutant.


Object Structure

As shown in Figure 3-18, each object has an object header and an object body. The object manager controls the object headers, and the owning executive components control the object bodies of the object types they create. In addition, each object header points to the list of processes that have the object open and to a special object called the type object that contains information common to each instance of the object.

Figure 3-18. Structure of an object


Object Headers and Bodies

The object manager uses the data stored in an object's header to manage objects without regard to their type. Table 3-4 briefly describes the object header attributes.

Table 3-4. Standard Object Header Attributes

Attribute

Purpose

Object name

Makes an object visible to other processes for sharing

Object directory

Provides a hierarchical structure in which to store object names

Security descriptor

Determines who can use the object and what they can do with it (Note: it might be null for objects without a name.)

Quota charges

Lists the resource charges levied against a process when it opens a handle to the object

Open handle count

Counts the number of times a handle has been opened to the object

Open handles list

Points to the list of processes that have opened handles to the object (not present for all objects)

Object type

Points to a type object that contains attributes common to objects of this type

Reference count

Counts the number of times a kernel-mode component has referenced the address of the object


In addition to an object header, each object has an object body whose format and contents are unique to its object type; all objects of the same type share the same object body format. By creating an object type and supplying services for it, an executive component can control the manipulation of data in all object bodies of that type.

The object manager provides a small set of generic services that operate on the attributes stored in an object's header and can be used on objects of any type (although some generic services don't make sense for certain objects). These generic services, some of which the Windows subsystem makes available to Windows applications, are listed in Table 3-5.

Table 3-5. Generic Object Services

Service

Purpose

Close

Closes a handle to an object

Duplicate

Shares an object by duplicating a handle and giving it to another process

Query object

Gets information about an object's standard attributes

Query security

Gets an object's security descriptor

Set security

Changes the protection on an object

Wait for a single object

Synchronizes a thread's execution with one object

Wait for multiple objects

Synchronizes a thread's execution with multiple objects


Although these generic object services are supported for all object types, each object has its own create, open, and query services. For example, the I/O system implements a create file service for its file objects, and the process manager implements a create process service for its process objects. Although a single create object service could have been implemented, such a routine would have been quite complicated, because the set of parameters required to initialize a file object, for example, differs markedly from that required to initialize a process object. Also, the object manager would have incurred additional processing overhead each time a thread called an object service to determine the type of object the handle referred to and to call the appropriate version of the service. For these reasons and others, the create, open, and query services are implemented separately for each object type.

Type Objects

Object headers contain data that is common to all objects but that can take on different values for each instance of an object. For example, each object has a unique name and can have a unique security descriptor. However, objects also contain some data that remains constant for all objects of a particular type. For example, you can select from a set of access rights specific to a type of object when you open a handle to objects of that type. The executive supplies terminate and suspend access (among others) for thread objects and read, write, append, and delete access (among others) for file objects. Another example of an object-type-specific attribute is synchronization, which is described shortly.

To conserve memory, the object manager stores these static, object-type-specific attributes once when creating a new object type. It uses an object of its own, a type object, to record this data. As Figure 3-19 illustrates, if the object-tracking debug flag (described in the "Windows Global Flags" section later in this chapter) is set, a type object also links together all objects of the same type (in this case the Process type), allowing the object manager to find and enumerate them, if necessary.

Figure 3-19. Process objects and the process type object


EXPERIMENT: Viewing Object Headers and Type Objects

You can see the list of type objects declared to the object manager with the Winobj tool from http://www.sysinternals.com. After running Winobj, open the \ObjectTypes directory, as shown here:



You can look at the process object type data structure in the kernel debugger by first identifying a process object with the !process command:

kd>  !process0  0 ****  NT  ACTIVE  PROCESS DUMP  **** PROCESS   8a4ce668   SessionId: none  Cid: 0004    Peb:  00000000     ParentCid:0000     DirBase:   00039000   ObjectTable: e1001c88  HandleCount:  474.     Image:System

Then execute the !object command with the process object address as the argument:

kd>!object 8a4ce668 Object:  8a4ce668   Type: (8a4ceca0) Process     ObjectHeader:   8a4ce650     HandleCount:   2  PointerCount: 89

Notice that the object header starts 0x18 (24 decimal) bytes prior to the start of the object body. You can view the object header with this command:

kd>dt_object_header  8a4ce650 nt!_OBJECT_HEADER   +0x000PointerCount     : 79   +0x004HandleCount      : 2   +0x004NextToFree       : 0x00000002   +0x008 Type            : 0x8a4ceca0   +0x00cNameInfoOffset   : 0''   +0x00dHandleInfoOffset : 0''   +0x00eQuotaInfoOffset  : 0''   +0x00fFlags            : 0x22'"'   +0x010ObjectCreateInfo : 0x80545620   +0x010QuotaBlockCharged : 0x80545620   +0x014SecurityDescriptor : 0xe10001dc   +0x018Body             : _QUAD

Now look at the object type data structure by obtaining its address from the Type field of the object header data structure:

kd>  dt  _object_type8a4ceca0 ntdll!_OBJECT_TYPE    +0x000Mutex            : _ERESOURCE    +0x038TypeList         : _LIST_ENTRY  [  0x8a4cecd8  -0x8a4cecd8]    +0x040Name             : _UNICODE_STRING "Process"    +0x048DefaultObject    : (null)    +0x04cIndex            : 5    +0x050TotalNumberOfObjects : 0x30    +0x054TotalNumberOfHandles : 0x1b4    +0x058HighWaterNumberOfObjects :  0x3f    +0x05cHighWaterNumberOfHandles :  0x1b8    +0x060TypeInfo         : _OBJECT_TYPE_INITIALIZER    +0x0acKey              : 0x636f7250    +0x0b0ObjectLocks      : [4] _ERESOURCE

The output shows that the object type structure includes the name of the object type, tracks the total number of active objects of that type, and tracks the peak number of handles and objects of that type. The TypeInfo field stores the pointer to the data structure that stores attributes common to all objects of the object type as well as pointers to the object type's methods:

kd>  dt  _object_type_initializer    8a4ceca0+60 ntdll!_OBJECT_TYPE_INITIALIZER    +0x000Length             : 0x4c    +0x002UseDefaultObject   : 0''    +0x003CaseInsensitive    : 0''    +0x004InvalidAttributes   : 0xb0    +0x008GenericMapping     : _GENERIC_MAPPING    +0x018ValidAccessMask    : 0x1f0fff    +0x01cSecurityRequired   : 0x1 ''    +0x01dMaintainHandleCount   : 0''    +0x01eMaintainTypeList :   0''    +0x020PoolType         :   0 ( NonPagedPool )    +0x024DefaultPagedPoolCharge   : 0x1000    +0x028DefaultNonPagedPoolCharge   : 0x288    +0x02cDumpProcedure      : (null)    +0x030OpenProcedure      : (null)    +0x034CloseProcedure     : (null)    +0x038DeleteProcedure    :  0x805abe6e     nt!PspProcessDelete+0    +0x03cParseProcedure     : (null)    +0x040SecurityProcedure   : 0x805cf682       nt!SeDefaultObjectMethod+0    +0x044QueryNameProcedure   : (null)    +0x048OkayToCloseProcedure  : (null)


Type objects can't be manipulated from user mode because the object manager supplies no services for them. However, some of the attributes they define are visible through certain native services and through Windows API routines. The attributes stored in the type objects are described in Table 3-6.

Table 3-6. Type Object Attributes

Attribute

Purpose

Type name

The name for objects of this type ("process," "event," "port," and so on)

Pool type

Indicates whether objects of this type should be allocated from paged or nonpaged memory

Default quota charges

Default paged and nonpaged pool values to charge to process quotas

Access types

The types of access a thread can request when opening a handle to an object of this type ("read," "write," "terminate," "suspend," and so on)

Generic access rights mapping

A mapping between the four generic access rights (read, write, execute, and all) to the type-specific access rights

Synchronization

Indicates whether a thread can wait for objects of this type

Methods

One or more routines that the object manager calls automatically at certain points in an object's lifetime


Synchronization, one of the attributes visible to Windows applications, refers to a thread's ability to synchronize its execution by waiting for an object to change from one state to another. A thread can synchronize with executive job, process, thread, file, event, semaphore, mutex, and timer objects. Other executive objects don't support synchronization. An object's ability to support synchronization is based on whether the object contains an embedded dispatcher object, a kernel object that is covered in the section "Low-IRQL Synchronization" later in this chapter.

Object Methods

The last attribute in Table 3-6, methods, comprises a set of internal routines that are similar to C++ constructors and destructors that is, routines that are automatically called when an object is created or destroyed. The object manager extends this idea by calling an object method in other situations as well, such as when someone opens or closes a handle to an object or when someone attempts to change the protection on an object. Some object types specify methods, whereas others don't, depending on how the object type is to be used.

When an executive component creates a new object type, it can register one or more methods with the object manager. Thereafter, the object manager calls the methods at well-defined points in the lifetime of objects of that type, usually when an object is created, deleted, or modified in some way. The methods that the object manager supports are listed in Table 3-7.

Table 3-7. Object Methods

Method

When Method Is Called

Open

When an object handle is opened

Close

When an object handle is closed

Delete

Before the object manager deletes an object

Query

name When a thread requests the name of an object, such as a file, that exists in a secondary object namespace

Parse

When the object manager is searching for an object name that exists in a secondary object namespace

Security

When a process reads or changes the protection of an object, such as a file, that exists in a secondary object namespace


The object manager calls the open method whenever it creates a handle to an object, which it does when an object is created or opened. However, only one object type, the Windowstation, defines an open method. The Windowstation object type requires an open method so that Win32k.sys can share a piece of memory with the process that serves as a desktop-related memory pool.

An example of the use of a close method occurs in the I/O system. The I/O manager registers a close method for the file object type, and the object manager calls the close method each time it closes a file object handle. This close method checks whether the process that is closing the file handle owns any outstanding locks on the file and, if so, removes them. Checking for file locks isn't something the object manager itself could or should do.

The object manager calls a delete method, if one is registered, before it deletes a temporary object from memory. The memory manager, for example, registers a delete method for the section object type that frees the physical pages being used by the section. It also verifies that any internal data structures the memory manager has allocated for a section are deleted before the section object is deleted. Once again, the object manager can't do this work because it knows nothing about the internal workings of the memory manager. Delete methods for other types of objects perform similar functions.

The parse method (and similarly, the query name method) allows the object manager to relinquish control of finding an object to a secondary object manager if it finds an object that exists outside the object manager namespace. When the object manager looks up an object name, it suspends its search when it encounters an object in the path that has an associated parse method. The object manager calls the parse method, passing to it the remainder of the object name it is looking for. There are two namespaces in Windows in addition to the object manager's: the registry namespace, which the configuration manager implements, and the file system namespace, which the I/O manager implements with the aid of file system drivers. (See Chapter 5 for more information on the configuration manager and Chapter 9 for more about the I/O manager and file system drivers.)

For example, when a process opens a handle to the object named \Device\Floppy0\ docs\resume.doc, the object manager traverses its name tree until it reaches the device object named Floppy0. It sees that a parse method is associated with this object, and it calls the method, passing to it the rest of the object name it was searching for in this case, the string \docs\resume.doc. The parse method for device objects is an I/O routine because the I/O manager defines the device object type and registers a parse method for it. The I/O manager's parse routine takes the name string and passes it to the appropriate file system, which finds the file on the disk and opens it.

The security method, which the I/O system also uses, is similar to the parse method. It is called whenever a thread tries to query or change the security information protecting a file. This information is different for files than for other objects because security information is stored in the file itself rather than in memory. The I/O system, therefore, must be called to find the security information and read or change it.

Object Handles and the Process Handle Table

When a process creates or opens an object by name, it receives a handle that represents its access to the object. Referring to an object by its handle is faster than using its name because the object manager can skip the name lookup and find the object directly. Processes can also acquire handles to objects by inheriting handles at process creation time (if the creator specifies the inherit handle flag on the CreateProcess call and the handle was marked as inheritable, either at the time it was created or afterward by using the Windows SetHandleInformation function) or by receiving a duplicated handle from another process. (See the Windows DuplicateHandle function.)

All user-mode processes must own a handle to an object before their threads can use the object. Using handles to manipulate system resources isn't a new idea. C and Pascal (and other language) run-time libraries, for example, return handles to opened files. Handles serve as indirect pointers to system resources; this indirection keeps application programs from fiddling directly with system data structures.

Note

Executive components and device drivers can access objects directly because they are running in kernel mode and therefore have access to the object structures in system memory. However, they must declare their usage of the object by incrementing the reference count so that the object won't be deallocated while it's still being used. (See the section "Object Retention" later in this chapter for more details.)


Object handles provide additional benefits. First, except for what they refer to, there is no difference between a file handle, an event handle, and a process handle. This similarity provides a consistent interface to reference objects, regardless of their type. Second, the object manager has the exclusive right to create handles and to locate an object that a handle refers to. This means that the object manager can scrutinize every user-mode action that affects an object to see whether the security profile of the caller allows the operation requested on the object in question.

EXPERIMENT: Viewing Open Handles

Run Process Explorer, and make sure the lower pane is enabled and configured to show open handles. (Click on View, Lower Pane View, and then Handles). Then open a command prompt and view the handle table for the new Cmd.exe process. You should see an open file handle to the current directory. For example, assuming the current directory is C:\, Process Explorer shows the following:



If you then change the current directory with the CD command, you will see in Process Explorer that the handle to the previous current directory is closed and a new handle is opened to the new current directory. The previous handle is highlighted briefly in red, and the new handle is highlighted in green. The duration of the highlight can be adjusted by clicking Options and then Difference Highlight Duration.

Process Explorer's differences highlighting feature makes it easy to see changes in the handle table. For example, if a process is leaking handles, viewing the handle table with Process Explorer can quickly show what handle or handles are being opened but not closed. This information can assist the programmer to find the handle leak.

You can also display the open handle table by using the command line Handle tool from http://www.sysinternals.com. For example, note the following partial output of Handle examining the handle table for a Cmd.exe process before and after changing the directory:

C:\>handle -p cmd.exe Handle v2.2 Copyright (C) 1997-2004 Mark Russinovich Sysinternals - www.sysinternals.com ------------------------------------------------------------ cmd.exe pid: 3184   BIGDAVID\dsolomon    b0: File            C:\ C:\>cd windows C:\WINDOWS>handle -p cmd.exe . . cmd.exe  pid: 3184  BIGDAVID\dsolomon    b4: File            C:\WINDOWS


An object handle is an index into a process-specific handle table, pointed to by the executive process (EPROCESS) block (described in Chapter 6). The first handle index is 4, the second 8, and so on. A process's handle table contains pointers to all the objects that the process has opened a handle to. Handle tables are implemented as a three-level scheme, similar to the way that the x86 memory management unit implements virtual-to-physical address translation, giving a maximum of more than 16,000,000 handles per process. (See Chapter 7 for details about memory management in x86 systems.)

In Windows 2000, when a process is created, the object manager allocates the top level of the handle table, which contains pointers to the middle-level tables; the middle level, which contains the first array of pointers to subhandle tables; and the lowest level, which contains the first subhandle table. Figure 3-20 illustrates the Windows 2000 handle table architecture. In Windows 2000, the object manager treats the low 24 bits of an object handle's value as three 8-bit fields that index into each of the three levels in the handle table. In Windows XP and Windows Server 2003, only the lowest level handle table is allocated on process creation the other levels are created as needed. In Windows 2000, the subhandle table consists of 255 usable entries. In Windows XP and Windows Server 2003, the subhandle table consists of as many entries as will fit in a page minus one entry that is used for handle auditing. For example, for x86 systems a page is 4096 bytes, divided by the size of a handle table entry (8 bytes), which is 512, minus 1, which is a total of 511 entries in the lowest level handle table. In Windows XP and Windows Server 2003, the mid-level handle table contains a full page of pointers to subhandle tables, so the number of subhandle tables depends on the size of the page and the size of a pointer for the platform.

Figure 3-20. Windows 2000 process handle table architecture


EXPERIMENT: Creating the Maximum Number of Handles

The test program Testlimit from http://www.sysinternals.com/windowsinternals.shtml has an option to open handles to an object until it cannot open any more handles. You can use this to see how many handles can be created in a single process on your system. Because handle tables are allocated from paged pool, you might run out of paged pool before you hit the maximum number of handles that can be created in a single process. To see how many handles you can create on your system, follow these steps:

  1. Download the Testlimit zip file from the link just mentioned, and unzip it into a directory.

  2. Run Process Explorer, and click View and then System Information. Notice the current and maximum size of paged pool. (To display the maximum pool size values, Process Explorer must be configured properly to access the symbols for the kernel image, Ntoskrnl.exe.) Leave this system information display running so that you can see pool utilization when you run the Testlimit program.

  3. Open a command prompt.

  4. Run the Testlimit program with the "-h" switch (do this by typing testlimit h). When Testlimit fails to open a new handle, it will display the total number of handles it was able to create. If the number is less than approximately 16 million, you are probably running out of paged pool before hitting the theoretical per-process handle limit.

  5. Close the command-prompt window; doing this will kill the Testlimit process, thus closing all the open handles.


As shown in Figure 3-21, on x86 systems, each handle entry consists of a structure with two 32-bit members: a pointer to the object (with flags), and the granted access mask. On 64-bit systems, a handle table entry is 12 bytes long: a 64-bit pointer to the object header and a 32bit access mask. (Access masks are described in Chapter 8.)

Figure 3-21. Structure of a handle table entry


On Windows 2000, the first 32-bit member contains both a pointer to the object header and four flags. Because object headers are always 8-byte aligned, the low-order 3 bits of this field are free for use as flags. An entry's high bit is used as a lock. When the object manager translates a handle to an object pointer, it locks the handle entry while the translation is in progress. Because all objects are located in the system address space, the high bit of the object pointer is set. (The addresses are guaranteed to be higher than 0x80000000 even on systems with the /3GB boot switch.) Thus, the object manager can keep the high bit clear when a handle table entry is unlocked and, in the process of locking the entry, set the bit and obtain the object's correct pointer value. The object manager needs to lock a process's entire handle table, using a handle table lock associated with each process, only when the process creates a new handle or closes an existing handle. In Windows XP and Windows Server 2003, the lock bit is the low-order bit of the object pointer. The flag that was stored in this low-order bit in Windows 2000 is now stored in an unused bit in the access mask.

The first flag indicates whether the caller is allowed to close this handle. The second flag is the inheritance designation that is, it indicates whether processes created by this process will get a copy of this handle in their handle tables. As already noted, handle inheritance can be specified on handle creation or later with the SetHandleInformation function. (This flag can also be specified with the Windows SetHandleInformation function.) The third flag indicates whether closing the object should generate an audit message. (This flag isn't exposed to Windows the object manager uses it internally.)

System components and device drivers often need to open handles to objects that user-mode applications shouldn't have access to. This is done by creating handles in the kernel handle table (referenced internally with the name ObpKernelHandleTable). The handles in this table are accessible only from kernel mode and in any process context. This means that a kernelmode function can reference the handle in any process context with no performance impact. The object manager recognizes references to handles from the kernel handle table when the high bit of the handle is set that is, when references to kernel-handle-table handles have values greater than 0x80000000. On Windows 2000, the kernel-handle table is an independent handle table, but on Windows XP and Windows Server 2003 the kernel-handle table also serves as the handle table for the System process.

EXPERIMENT: Viewing the Handle Table with the Kernel Debugger

The !handle command in the kernel debugger takes three arguments:

!handle <handle index> <flags> <processid>

The handle index identifies the handle entry in the handle table. (Zero means display all handles.) The first handle is index 4, the second 8, and so on. For example, typing !handle 4 will show the first handle for the current process.

The flags you can specify are a bitmask, where bit 0 means display only the information in the handle entry, bit 1 means display free handles (not just used handles), and bit 2 means display information about the object that the handle refers to. The following command displays full details about the handle table for process ID 0x408:

kd>  !handle0  7408 processor number 0 Searching for Process with Cid == 408 PROCESS 865f0790 SessionId: 0 Cid:  0408  Peb: 7ffdf000   ParentCid: 01dc     DirBase: 04fd3000  ObjectTable:  856ca888   TableSize:   21.     Image: i386kd.exe Handle Table at e2125000  with 21  Entries in  use 0000: free  handle 0004: Object: e20da2e0  GrantedAccess: 000f001f Object: e20da2e0  Type: (81491b80) Section     ObjectHeader: e20da2c8         HandleCount: 1  PointerCount: 1 0008: Object: 80b13330  GrantedAccess: 00100003 Object: 80b13330  Type: (81495100) Event     ObjectHeader: 80b13318         HandleCount: 1  PointerCount: 1


Object Security

When you open a file, you must specify whether you intend to read or to write. If you try to write to a file that is opened for read access, you get an error. Likewise, in the executive, when a process creates an object or opens a handle to an existing object, the process must specify a set of desired access rights that is, what it wants to do with the object. It can request either a set of standard access rights (such as read, write, and execute) that apply to all object types or specific access rights that vary depending on the object type. For example, the process can request delete access or append access to a file object. Similarly, it might require the ability to suspend or terminate a thread object.

When a process opens a handle to an object, the object manager calls the security reference monitor, the kernel-mode portion of the security system, sending it the process's set of desired access rights. The security reference monitor checks whether the object's security descriptor permits the type of access the process is requesting. If it does, the reference monitor returns a set of granted access rights that the process is allowed, and the object manager stores them in the object handle it creates. How the security system determines who gets access to which objects is explored in Chapter 8.

Thereafter, whenever the process's threads use the handle, the object manager can quickly check whether the set of granted access rights stored in the handle corresponds to the usage implied by the object service the threads have called. For example, if the caller asked for read access to a section object but then calls a service to write to it, the service fails.

Object Retention

There are two types of objects: temporary and permanent. Most objects are temporary that is, they remain while they are in use and are freed when they are no longer needed. Permanent objects remain until they are explicitly freed. Because most objects are temporary, the rest of this section describes how the object manager implements object retention that is, retaining temporary objects only as long as they are in use and then deleting them. Because all user-mode processes that access an object must first open a handle to it, the object manager can easily track how many of these processes, and even which ones, are using an object. Tracking these handles represents one part in implementing retention. The object manager implements object retention in two phases. The first phase is called name retention, and it is controlled by the number of open handles to an object that exist. Every time a process opens a handle to an object, the object manager increments the open handle counter in the object's header. As processes finish using the object and close their handles to it, the object manager decrements the open handle counter. When the counter drops to 0, the object manager deletes the object's name from its global namespace. This deletion prevents new processes from opening a handle to the object.

The second phase of object retention is to stop retaining the objects themselves (that is, to delete them) when they are no longer in use. Because operating system code usually accesses objects by using pointers instead of handles, the object manager must also record how many object pointers it has dispensed to operating system processes. It increments a reference count for an object each time it gives out a pointer to the object; when kernel-mode components finish using the pointer, they call the object manager to decrement the object's reference count. The system also increments the reference count when it increments the handle count, and likewise decrements the reference count when the handle count decrements, because a handle is also a reference to the object that must be tracked. (For further details on object retention, see the DDK documentation on the functions ObReferenceObjectByPointer and ObDereferenceObject.)

Figure 3-22 illustrates two event objects that are in use. Process A has the first event open. Process B has both events open. In addition, the first event is being referenced by some kernelmode structure; thus, the reference count is 3. So even if processes A and B closed their handles to the first event object, it would continue to exist because its reference count is 1. However, when process B closes its handle to the second event object, the object would be deallocated.

Figure 3-22. Handles and reference counts


So even after an object's open handle counter reaches 0, the object's reference count might remain positive, indicating that the operating system is still using the object. Ultimately, when the reference count drops to 0, the object manager deletes the object from memory.

Because of the way object retention works, an application can ensure that an object and its name remain in memory simply by keeping a handle open to the object. Programmers who write applications that contain two or more cooperating processes need not be concerned that one process might delete an object before the other process has finished using it. In addition, closing an application's object handles won't cause an object to be deleted if the operating system is still using it. For example, one process might create a second process to execute a program in the background; it then immediately closes its handle to the process. Because the operating system needs the second process to run the program, it maintains a reference to its process object. Only when the background program finishes executing does the object manager decrement the second process's reference count and then delete it.

Resource Accounting

Resource accounting, like object retention, is closely related to the use of object handles. A positive open handle count indicates that some process is using that resource. It also indicates that some process is being charged for the memory the object occupies. When an object's handle count and reference count drop to 0, the process that was using the object should no longer be charged for it.

Many operating systems use a quota system to limit processes' access to system resources. However, the types of quotas imposed on processes are sometimes diverse and complicated, and the code to track the quotas is spread throughout the operating system. For example, in some operating systems, an I/O component might record and limit the number of files a process can open, whereas a memory component might impose a limit on the amount of memory a process's threads can allocate. A process component might limit users to some maximum number of new processes they can create or a maximum number of threads within a process. Each of these limits is tracked and enforced in different parts of the operating system.

In contrast, the Windows object manager provides a central facility for resource accounting. Each object header contains an attribute called quota charges that records how much the object manager subtracts from a process's allotted paged and/or nonpaged pool quota when a thread in the process opens a handle to the object.

Each process on Windows points to a quota structure that records the limits and current values for nonpaged pool, paged pool, and page file usage. (Type dt nt!_EPROCESS_QUOTA_ENTRY in the kernel debugger to see the format of this structure.) These quotas default to 0 (no limit) but can be specified by modifying registry values. (See NonPagedPoolQuota, PagedPoolQuota, and PagingFileQuota under HKLM\System\CurrentControlSet\Session Manager\Memory Management.) Note that all the processes in an interactive session share the same quota block (and there's no documented way to create processes with their own quota blocks).

Object Names

An important consideration in creating a multitude of objects is the need to devise a successful system for keeping track of them. The object manager requires the following information to help you do so:

  • A way to distinguish one object from another

  • A method for finding and retrieving a particular object

The first requirement is served by allowing names to be assigned to objects. This is an extension of what most operating systems provide the ability to name selected resources, files, pipes, or a block of shared memory, for example. The executive, in contrast, allows any resource represented by an object to have a name. The second requirement, finding and retrieving an object, is also satisfied by object names. If the object manager stores objects by name, it can find an object by looking up its name.

Object names also satisfy a third requirement, which is to allow processes to share objects. The executive's object namespace is a global one, visible to all processes in the system. One process can create an object and place its name in the global namespace, and a second process can open a handle to the object by specifying the object's name. If an object isn't meant to be shared in this way, its creator doesn't need to give it a name.

To increase efficiency, the object manager doesn't look up an object's name each time someone uses the object. Instead, it looks up a name under only two circumstances. The first is when a process creates a named object: the object manager looks up the name to verify that it doesn't already exist before storing the new name in the global namespace. The second is when a process opens a handle to a named object: the object manager looks up the name, finds the object, and then returns an object handle to the caller; thereafter, the caller uses the handle to refer to the object. When looking up a name, the object manager allows the caller to select either a case-sensitive or a case-insensitive search, a feature that supports POSIX and other environments that use case-sensitive filenames.

Where the names of objects are stored depends on the object type. Table 3-8 lists the standard object directories found on all Windows systems and what types of objects have their names stored there. Of the directories listed, only \BaseNamedObjects and \GLOBAL?? (\?? on Windows 2000) are visible to user programs (see the Session Namespace section later in this chapter for more information).

Table 3-8. Standard Object Directories

Directory

Types of Object Names Stored

\GLOBAL?? (\?? in Windows 2000)

MS-DOS device names (\DosDevices is a symbolic link to this directory.)

\BaseNamedObjects

Mutexes, events, semaphores, waitable timers, and section objects

\Callback

Callback objects

\Device

Device objects

\Driver

Driver objects

\FileSystem

File system driver objects and file system recognizer device objects

\KnownDlls

Section names and path for known DLLs (DLLs mapped by the system at startup time)

\Nls

Section names for mapped national language support tables

\ObjectTypes

Names of types of objects

\RPC Control

Port objects used by remote procedure calls (RPCs)

\Security

Names of objects specific to the security subsystem

\Windows

Windows subsystem ports and window stations


Because the base kernel objects such as mutexes, events, semaphores, waitable timers, and sections have their names stored in a single object directory, no two of these objects can have the same name, even if they are of a different type. This restriction emphasizes the need to choose names carefully so that they don't collide with other names (for example, prefix names with your company and product name).

Object names are global to a single computer (or to all processors on a multiprocessor computer), but they're not visible across a network. However, the object manager's parse method makes it possible to access named objects that exist on other computers. For example, the I/O manager, which supplies file object services, extends the functions of the object manager to remote files. When asked to open a remote file object, the object manager calls a parse method, which allows the I/O manager to intercept the request and deliver it to a network redirector, a driver that accesses files across the network. Server code on the remote Windows system calls the object manager and the I/O manager on that system to find the file object and return the information back across the network.

EXPERIMENT: Looking at the Base Named Objects

You can see the list of base objects that have names with the Winobj tool from http://www.sysinternals.com. Run Winobj.exe and click on \BaseNamedObjects, as shown here:



The named objects are shown on the right. The icons indicate the object type.

  • Mutexes are indicated with a stop sign.

  • Sections (Windows file mapping objects) are shown as memory chips.

  • Events are shown as exclamation points.

  • Semaphores are indicated with an icon that resembles a traffic signal.

  • Symbolic links have icons that are curved arrows.


Object directories The object directory object is the object manager's means for supporting this hierarchical naming structure. This object is analogous to a file system directory and contains the names of other objects, possibly even other object directories. The object directory object maintains enough information to translate these object names into pointers to the objects themselves. The object manager uses the pointers to construct the object handles that it returns to user-mode callers. Both kernel-mode code (including executive components and device drivers) and user-mode code (such as subsystems) can create object directories in which to store objects. For example, the I/O manager creates an object directory named \Device, which contains the names of objects representing I/O devices.

Symbolic links In certain file systems (on NTFS and some UNIX systems, for example), a symbolic link lets a user create a filename or a directory name that, when used, is translated by the operating system into a different file or directory name. Using a symbolic link is a simple method for allowing users to indirectly share a file or the contents of a directory, creating a cross-link between different directories in the ordinarily hierarchical directory structure.

The object manager implements an object called a symbolic link object, which performs a similar function for object names in its object namespace. A symbolic link can occur anywhere within an object name string. When a caller refers to a symbolic link object's name, the object manager traverses its object namespace until it reaches the symbolic link object. It looks inside the symbolic link and finds a string that it substitutes for the symbolic link name. It then restarts its name lookup.

One place in which the executive uses symbolic link objects is in translating MS-DOS-style device names into Windows internal device names. In Windows, a user refers to floppy and hard disk drives using the names A:, B:, C:, and so on and serial ports as COM1, COM2, and so on. The Windows subsystem makes these symbolic link objects protected, global data by placing them in the object manager namespace under the \?? object directory on Windows 2000 and the \Global?? directory on Windows XP and Windows Server 2003.

Session Namespace

Windows NT was originally written with the assumption that only one user would log on to the system interactively and that the system would run only one instance of any interactive application. The addition of Windows Terminal Services in Windows 2000 Server and fast user switching in Windows XP changed these assumptions, thus requiring changes to the object manager namespace model to support multiple users. (For a basic description of terminal services and sessions, see Chapter 1.)

A user logged on to the console session has access to the global namespace, a namespace that serves as the first instance of the namespace. Additional sessions are given a session-private view of the namespace known as a local namespace. The parts of the namespace that are localized for each session include \DosDevices, \Windows, and \BaseNamedObjects. Making separate copies of the same parts of the namespace is known as instancing the namespace. Instancing \DosDevices makes it possible for each user to have different network drive letters and Windows objects such as serial ports. On Windows 2000, the global \DosDevices directory is named \?? and is the directory to which the \DosDevices symbolic link points, and local \DosDevices directories are identified by the session id for the terminal server session. On Windows XP and later, the global \DosDevices directory is named \Global?? and is the directory to which \DosDevices points, and local \DosDevices directories are identified by the logon session ID.

The \Windows directory is where Win32k.sys creates the interactive window station, \WinSta0. A Terminal Services environment can support multiple interactive users, but each user needs an individual version of WinSta0 to preserve the illusion that he or she is accessing the predefined interactive window station in Windows. Finally, applications and the system create shared objects in \BaseNamedObjects, including events, mutexes, and memory sections. If two users are running an application that creates a named object, each user session must have a private version of the object so that the two instances of the application don't interfere with one another by accessing the same object.

The object manager implements a local namespace by creating the private versions of the three directories mentioned under a directory associated with the user's session under \Sessions\X (where X is the session identifier). When a Windows application in remote session two creates a named event, for example, the object manager transparently redirects the object's name from \BaseNamedObjects to \Sessions\2\BaseNamedObjects.

All object manager functions related to namespace management are aware of the instanced directories and participate in providing the illusion that nonconsole sessions use the same namespace as the console session. Windows subsystem DLLs prefix names passed by Windows applications that reference objects in \DosDevices with \?? (for example, C:\Windows becomes \??\C:\Windows). When the object manager sees the special \?? prefix, the steps it takes depends on the version of Windows, but it always relies on a field named DeviceMap in the executive process object (EPROCESS, which is described further in Chapter 6) that points to a data structure shared by other processes in the same session. The DosDevicesDirectory field of the DeviceMap structure points at the object manager directory that represents the process's local \DosDevices. The target directory varies depending on the system:

  • If the system is Windows 2000 and Terminal Services are not installed, the DosDevicesDirectory field of the DeviceMap structure of the process points at the \?? directory because there are no local namespaces.

  • If the system is Windows 2000 and Terminal Services are installed, when a new session becomes active the system copies all the objects from the global \?? directory into the session's local \Devices directory and the DosDevicesDirectory field of the DeviceMap structure points at the local directory.

  • On Windows XP and Windows Server 2003, the system does not make copies of global objects in the local DosDevices directories. When the object manager sees a reference to \??, it locates the process's local \DosDevices by using the DosDevicesDirectory field of the DeviceMap. If the object manager doesn't find the object in that directory, it checks the DeviceMap field of the directory object, and if it's valid it looks for the object in the directory pointed to by the GlobalDosDevicesDirectory field of the DeviceMap structure, which is always \Global??.

Under certain circumstances, applications that are Terminal Services aware need to access objects in the console session even if the application is running in a remote session. The application might want to do this to synchronize with instances of itself running in other remote sessions or with the console session. For these cases, the object manager provides the special override "\Global" that an application can prefix to any object name to access the global namespace. For example, an application in session two opening an object named \Global\ApplicationInitialized is directed to \BasedNamedObjects\ApplicationInitialized instead of \Sessions\2\BaseNamedObjects\ApplicationInitialized.

On Windows XP and Windows Server 2003, an application that wants to access an object in the global \DosDevices directory does not need to use the \Global prefix as long as the object doesn't exist in its local \DosDevices directory. This is because the object manager will automatically look in the global directory for the object if it doesn't find it in the local directory. However, an application running on Windows 2000 with Terminal Services must always specify the \Global prefix to access objects in the global \DosDevices directory.

EXPERIMENT: Viewing Namespace Instancing

You can see the object manager instance of the namespace by creating a session other than the console session and then viewing the handle table for a process in that session. On Windows XP Home Edition or on a Windows XP Professional system that is not a member of a domain, disconnect the console session (by clicking Start, clicking Log Off, and choosing Disconnect and Switch User, or by pressing the Windows key + L) and logging in to a new account. If you have a Windows 2000 Server, Advanced Server, or Datacenter Server system, run the Terminal Services client, connect to the server, and log in.

Once you are logged in to the new session, run Winobj.exe from http://www.sysinternals.com and click on the \Sessions directory. You'll see a subdirectory with a numeric name for each active remote session. If you open one of these directories, you'll see subdirectories named \DosDevices, \Windows, and \BaseNamedObjects, which are the local namespace subdirectories of the session. The following screen shot shows a local namespace:



Next run Process Explorer and select a process in the new session (such as Explorer.exe), and then view the handle table (by clicking View, Lower Pane View, and then Handles). You should see a handle to \Windows\Windowstations\WinSta0 underneath \Sessions\n, where n is the session id. Objects with global names will appear under \Sessions\n\BaseNamedObjects.


     < Day Day Up > 


    Microsoft Windows Internals
    Microsoft Windows Internals (4th Edition): Microsoft Windows Server 2003, Windows XP, and Windows 2000
    ISBN: 0735619174
    EAN: 2147483647
    Year: 2004
    Pages: 158

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net