Windows program design and windows core programming (kernel object theory)
First, what is a kernel object?
As a Windows software developer, you often need to create, open, and operate various kernel objects. To create and operate several types of kernel objects, such as access symbol objects, event objects, file objects, file mapping objects, I / O completion port objects, job objects, mailbox objects, mutual exclusive objects, pipe objects, process objects, Beacon objects, thread objects, and waiting timer objects, etc. These objects are created by calling functions. For example, the CreateFilemapping function enables the system to create a file mapping object. Each kernel object is just a memory block allocated by the kernel and can only be accessed by the kernel. The memory block is a data structure that is responsible for maintaining various information of the object. Some data members (such as security descriptors, usage, etc.) are the same in all object types, but most data members belong to a specific object type. For example, a process object has a process I d, a basic priority, and an exit code, while the file object has one byte displacement, a shared mode, and an open mode. Since the data structure of the kernel object can only be accessed by kernel, the application cannot find these data structures in memory and change their content directly. Microsoft specifies this limiting condition to ensure that the kernel object structure is maintained. This limitation also enables Microsoft to add, delete, and modify data members without destroying any application.
If we can't change these data structures directly, how can our applications operate these kernel objects? The solution is that Windows provides a set of functions to operate these structures with a well-defined way. These kernel objects can always be accessed through these functions. When a function for creating a kernel object is called, the function returns a handle for identifying the object. The handle can be considered an opaque value, and this value can be used in any thread in your process. Pass this handle to each function of Windows, so that the system can know which kernel object you want to do.
1, the use count of kernel objects
The kernel object is owned by the kernel, not by the process. In other words, if your process calls a function that creates a kernel object, then your process is terminated, then the kernel object is not necessarily revoked. In most cases, the object will be undone, but if another process is using the kernel object created by your process, then the kernel knows that do not undo the object before stopping using the object, you must remember The existence time of the kernel object can be longer than the process of creating the object.
2, safety
The kernel object can be protected by a security descriptor. The security descriptor is used to describe who created the object, who can access or use the object, who is not entitled to access the object. Creating a kernel object is identified by the Security_Attributes structure. The reference source code can be learned when actually programmed.
Second, the kernel object handle of the process
When a process is initialized, the system has to assign it a handle table for it. This handle is only used for kernel objects, not for user objects or GDI objects. The detailed structure and management method of the handle is not described. It may be as follows:
1. Create a kernel object
When you create a kernel object, such as: Handle H = CreateMuteX (Null, False, "HEHE"); return value (handle value) is the index in the above table, and the block pointer in the kernel object is filled in the second column. .
2, turn off the kernel object
No matter how to create a kernel object, you must indicate the operation of the object by calling C L O S E H A N D L E:
Bool CloseHandle (Handle Hobj);
This function first checks the handle table of the calling process to ensure that the index (handle) passed to it is used to identify the object that is actually accessible. If the index is valid, the system can obtain the address of the data structure of the kernel object, and determine the data member of the usage count in the structure. If the usage count is 0, the kernel has revoked the kernel object from the memory. Third, cross the process boundary shared kernel object
1. By inheriting the shared object handle
By exposing the available kernel object handle of the parent process in the process of creating a child process. Inheritance implementation requires two steps:
A. The parent process should clearly indicate that this object is inherited when creating a kernel object:
Security_attributes sa;
Sa.nlength = SizeOf (SA);
Sa.lpsecurityDescriptor = null;
Sa.binherithandle = true;
Handle Hmutex = Createmutex (& SA, FALSE, NULL);
...
B. When the parent process is generated when generating a child process, it is clear that the sub-process can inherit your kernel object:
Bool createprocess
LPCTSTR LPAPPLICATIONNAME,
// Pointer to Name of Executable Module
LPTSTR LPCOMMANDLINE, / / POINTER TO COMMAND LINE STRING
LPSecurity_attributes LPPROCESSATTRIBUTES,
// Process Security Attributes
LPSecurity_attributes LPTHREADATIADATTRIBUTES,
// Thread Security Attributes, THREAD SECURITY
Bool binherithandles, // Handle Inheritance Flag
DWORD dwcreationFlags, // Creation Flags
LPVOID LPENVIRONMENT, / / POINTER TO New Environment Block
LPCTSTR LPCURRENTDIRECTORY, / / POINTER TO CURRENT DIRECTORY NAME
LPStartupinfo LPStartupinfo, // Pointer to Startupinfo
LPPROCESS_INFORMATION LPPROCESSINFORMATION
// Pointer to Process_information
);
When the parameter binheritHandle is True, the child process can inherit the inheritance handle value of the parent process. However, it is not allowed to start executing its code immediately. Of course, the system creates a new and empty handle table for the child process, just as it creates a handle table for any new process. However, since True is passed to the BinheritHandles parameter of CreateProcess, another operation is to be used, that is, it is necessary to traverse the handle table of the parent process, and the system will system will The project accurately copies into the sub-process handle. The item copy to the sub-process's handle table will be identical to the position in the handle of the parent process. This situation is very important because it means that in the parent process and sub-process, the handle value identifies the kernel object is the same. In addition to copying sentences, the system also incremented the usage count of kernel objects because two processes use this object. If you want to revoke kernel objects, then the parent process and child process must call the number of CloseHandle on the object, or terminate the operation of the process. The child process does not have to terminate it first, but the parent process does not have to terminate it first. In fact, the CREATEPROCESS function returns, the parent process can immediately turn off the handle of the object without affecting the ability of the sub-process to operate the object. It should be understood that the inheritance of the object handle can only be used when generating a child process. If the parent process is ready to create a new kernel object with the inherited handle, the sub-process that has been run will not be able to inherit these new handles. Here, a function is used to change the handle:
Bool SetHandleinformation (Handle Hobject,
DWORD DWMASK,
DWORD DWFLAGS;
The first parameter hobject is used to identify a valid handle. The second parameter dwMask tells the function who wants to change which or the flags. There are currently two markers associated with each handle:
#define Handle_Flag_inherit 0x00000001
#define Handle_Flag_Protect_From_Close 0x00000002
The third parameter is D w f L a g s, which is used to indicate what value wants to set this flag.
2, share the kernel object by named object
Many (although not all) kernel objects are named. Such as:
Handle Createmutex
Psecurity_attributes PSA,
Bool Binitialowner,
PCTSTR PSZNAME);
The last parameter pszname is the name of this kernel object. When the NULL is passed for this parameter, you will specify a unnamed (anonymous) kernel object to the system. When you create an unnamed object, you can share the object of the process by using inherited DuplicateHand Le (hereinafter will be described). To share objects by name, you must give an object to an object.
Another way to share objects by name is that the process does not call the create * function, but to call some of the Open * function such as:
Handle OpenMuteX
Psecurity_attributes PSA,
Bool Binitialowner,
PCTSTR PSZNAME);
The main difference between the CREATE * function and the call Open * function is that if the object does not exist, the CREATE * function will create the object, and the Open * function failed.
3. Share the kernel object by copying the object handle
The last way to share the kernel objects across the process boundary is to use the duplicateHandle function:
Bool DuplicateHandle
Handle HsourceProcessHandle, // Handle to the Source Process
Handle HsourceHandle, // Handle To DuplicateHandle HtargetProcessHandle, // Handle To Process To Duplicate To
LPHANDLE LPTARGETHANDLE, / / POINTER TO DUPLICATE HANDLE
DWORD DWDESIREDACCESS, // Access for Duplicate Handle
Bool BinheritHandle, // Handle Inheritance Flag
DWORD DWOPTIONS // Optional Action
);
Simply put, the function takes out the item in the handle of the process and copy the item into the handle of another process. D u pl i c a t e h a n d l e function is equipped with several parameters, but it is actually very simple. D u P L I C A TEME H A N D L E Function The most common usage should involve three different processes running in the system. Specific programming details can be referred to related routines.