续上......
内核对象如何在进程边界共享?
1.对象句柄的继承
当进程具有父子关系,就能使用对象句柄的继承性。父进程有一个或多个对象句柄,并且父进程可以决定生成一个子进程,为子进程赋于父进程的内核对象的访问权。
具体实现:
在安全描述符中指定,如:
SECURITY_ATTRIGBUTES sa
sa.nLength
=
sizeof
(sa);
sa.lpSecuntyDescriptor
=
null
;
sa.bInherithandle
=
TRUE;
//
指定子进程可以继承该父进程的内核对象。
HANDLE hMutex
=
CreateMutex(@sa,FALSE,NULL);
然后,在进程中使用CreateProcess函数:
BOOL CreateProcess(
PCTSTR pszApplicationName,
PTSTR pszCommandLine,
PSECURITY_ATTRIBUTES psaProcess,
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles,
DWORD fdwCreale,
PVOIO pvEnvironment,
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
PPROCESS_INFORMATION ppiProcInfo);
为bInheritHandles指定TRUE,则告诉系统,子进程继承父进程的句柄表中的可继承句柄。
2.改变句柄标志:
因为每个进程都会有个内核对象句柄表,指明该进程所拥有的句柄信息。那么,我们可以直接设置进程的句柄表,以获取某个内核对象的访问权。
句柄表的样式:
索引 |
内核对象内存块的指针 |
访问屏蔽(标志位的D W O R D ) |
标志(标志位的D W O R D ) |
1 |
0 x 0 0 0 0 0 0 0 0 |
(无) |
(无) |
2 |
0 x 0 0 0 0 0 0 0 0 |
(无) |
(无) |
3 |
0 x F 0 0 0 0 0 1 0 |
0 x ? ? ? ? ? ? ? ? |
0 x 0 0 0 0 0 0 0 1 |
设置句柄标志的函数:
BOOL SetHandleInformation(
HANDLE hObject,
DWORD dwMask,
DWORD dwFlags);
可以看到,该函数拥有3 个参数。第一个参数h O b j e c t 用于标识一个有效的句柄。第二个参数d w M a s k 告诉该函数想要改变哪个或那几个标志。目前有两个标志与每个句柄相关联:
#define
HANDLE FLAG_INHERIT 0x00000001
#define
HANDLE FLAG PROTECT FROM CLOSE 0x00000002
比如,打开一个内核对象的继承标志,则可以这样:
SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
关闭该标志,则可以:
SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, 0);
3.给内核对象命名
创建许多内核对象可以进行命名,如:
HANDLE CreateMutex(
PSLCURITY_ATTRIBUTES psa,
BOOL bInitialOwner,
PCTSTR pszName);
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL bManualReset,
BOOL bInitialState,
PCTSTR pszName);
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTES psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszNarne);
HANDLE CreateWaitableTimer(
PSLCURITY_ATTRIBUTES psa,
BOOL bManualReset,
PCTSTR pszName);
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
如果创建时最后的参数设置NULL,则创建一个末名命的内核对象当创建一个未命名的对象时,可以通过使用继承性(如上一节介绍的那样)或D u p l i c a t e H a n d l e (下一节将要介绍)共享跨越进程的对象。若要按名字共享对象,必须为对象赋予一个名字
实现:我们在ProcessA中创建一个内核对象:
HANDLE hMutexPronessA = CreateMutex(NULL, FALSE, "JeffMutex");
然后,在ProcessB中创建一个与ProcessA中同名的内核对象:
HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, "JeffMutex");
下面,会发生什么情况?以下是书中所揭示的:
当Process B 调用C r e a t e M u t e x 时,系统首先要查看是否已经存在一个名字为“J e ff M u t e x ”的内核对象。由于确实存在一个带有该名字的对象,因此内核要检查对象的类型。由于试图创建一个互斥对象,而名字为“J e ff M u t e x ”的对象也是个互斥对象,因此系统会执行一次安全检查,以确定调用者是否拥有对该对象的完整的访问权。如果拥有这种访问权,系统就在Process B 的句柄表中找出一个空项目,并对该项目进行初始化,使该项目指向现有的内核对象。如果该对象类型不匹配,或者调用者被拒绝访问,那么C r e a t e M u t e x 将运行失败(返回N U L L )。当Process B 对C r e a t e M u t e x 的调用取得成功时,它并不实际创建一个互斥对象。相反,Process B 只是被赋予一个与进程相关的句柄值,用于标识内核中现有的互斥对象。当然,由于Process B 的句柄表中的一个新项目要引用该对象,互斥对象的使用计数就会递增。在Process A和Process B 同时关闭它们的对象句柄之前,该对象是不会被撤消的。请注意,这两个进程中的句柄值很可能是不同的值。这是可以的。Process A 将使用它的句柄值,而Process B 则使用它自己的句柄值来操作一个互斥内核对象。
按名字共享对象的另一种方法是,进程不调用C r e a t e *函数,而是调用下面显示的O p e n *函数中的某一个,如:
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenFileMapping(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
调用Create*与调用Open*来创建打开内核对象有什么区别呢?
调用C r e a t e *函数与调用O p e n *函数之间的主要差别是,如果对象并不存在,那么C r e a t e *函数将创建该对象,而O p e n *函数则运行失败。
4.复制内核对象句柄:
共享跨越进程边界的内核对象的最后一个方法是使用D u p l i c a t e H a n d l e 函数:
BOOL DuplicateHandle(
HANDLE hSourceProcessHandle,
HANDLE hSourceHandle,
HANDLE hTargetProcessHandle,
PHANDLE phTargetHandle,
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwOptions);
简单说来,该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄表中。
二,进程
1.何为进程,进程有何特性?
所谓进程,就是一个正在运行程序的实例。它由两部分组成:
• 一个是操作系统用来管理进程的内核对象。
• 另一个是地址空间,它包含所有可执行模块或D L L 模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。
注意,进程是不活泼的,即进程完成的操作,是需要靠进程中的线程来完成。每个进程都必须有一个线程,也可以有多个线程。线程负责执行包含在进程地址空间内的代码。所谓多线程,就是多个线程同时执行包含在地址空间内的代码。
若要使线程执行进程内的代码,操作系统必须为线程分配CPU时间片。它是以一种循环的方式来提供时间片的,即给每个线程多少时间,时间完成之后,收回CPU,转交给另外的线程。
创建一个进程,系统会自动为其进程创建一个主线程。然后,可利用该线程可以创建其它的线程,其正是多线程。