Sharing Kernel Objects Across Process Boundaries

 

Sharing Kernel Objects Across Process Boundaries

 

在个进程边界之间共享内核对象。

Frequently, threads running in different processes need to share kernel objects. Here are some of the reasons why:

线程在运行时经常需要共享内核对象,原因有一下几点:

  • File-mapping objects allow you to share blocks of data between two processes running on a single machine.
  • 文件映射对象允许你在档台机器上的多个进程之间共享大块数据。
  • Mailslots and named pipes allow applications to send blocks of data between processes running on different machines connected to the network.
  • 邮槽和命名管道允许应用在不同机器上通过网络在进程之间发送大块数据。
  • Mutexes, semaphores, and events allow threads in different processes to synchronize their continued execution, as in the case of an application that needs to notify another application when it has completed some task.

互斥对象,信号量,和事件允许不同进程之间的相称同步执行任务,因为可能存在一种情况,在该情况下,一个应用需要通知其他应用它已经完成了某任务。

Because kernel object handles are process-relative, performing these tasks is difficult.

因为内核对象句柄和进程之间的相关性,完成以上任务是一件很困难的事情。

 However, Microsoft had several good reasons for designing the handles to be process-relative.

     但是,微软有足够的理由将内核对象句柄设计为进程相关。

The most important reason was robustness.

     最重要的理由就是系统的健壮性。

If kernel object handles were systemwide values, one process could easily obtain the handle to an object that another process was using and wreak havoc on that process.

如果内核对象句柄是系统级变量,一个进程能够轻易的获得其他进程正在使用中的对象并对其他进程进行破坏。

Another reason for process-relative handles is security.

安全是句柄进程相关的另外一个原因。

 Kernel objects are protected with security, and a process must request permission to manipulate an object before attempting to manipulate it.

   在内核对象的安全机制保护下,一个进程要修改其他进程的内核对象前必须经过允许。

 The creator of the object can prevent an unauthorized user from touching the object simply by denying access to it.

对象的创建者简单的阻止未授权的用户获取当前内核对象。

 

 

要在不同进程之间共享内核对象是一项比较困难的工作,因为window内核对象是进程相关的。

出于系统和应用程序的稳定和系统安全考虑,内核对象的进程相关性是必须的。

 

 

In the following section, we'll look at the three different mechanisms that allow processes to share kernel objects: using object handle inheritance, naming objects, and duplicating object handles.

 

在接下来的章节里,我们将看到三种不同的的内核对象共享结构:使用内核对象句柄的继承机制,命名对象和对象句柄复制。1

 

 

Using Object Handle Inheritance

使用内核对象句柄的继承

Object handle inheritance can be used only when processes have a parent-child relationship.对象句柄集成机制只能用在进程之间存在父子关系的情况下。

 

在此场景之下,父进程可能拥有一个或者多个内核对象,并且,父进程产生了多个子进程 ,该子进程被赋予存取父进程内核对象的权限。

For this type of inheritance to work, the parent process must perform several steps.

要想让这种权限继承机制工作,父进程必须进行以下步骤:

 

First, when the parent process creates a kernel object, the parent must indicate to the system that it wants the object's handle to be inheritable. Sometimes I hear people use the term object inheritance. However, there is no such thing as object inheritance; Windows supports object handle inheritance. In other words, it is the handles that are inheritable, not the objects themselves.

首先,但父进程创建了一个内核对象时,父进程必须让操作系统知道他的内核对象是可继承的。有是我听说人们使用对象继承这个术语。但是,对象继承是不存在的;Window系统支持对象句柄的继承。换句话说,他是对象句柄的继承,而不是人们所说的对象继承。

To create an inheritable handle, the parent process must allocate and initialize a SECURITY_ATTRIBUTES structure and pass the structure's address to the specific Create function. The following code creates a mutex object and returns an inheritable handle to it:

要想创建一个可继承的对象句柄,父进程必须分配并且初始化SECURITY_ATTRIBUTES结构体并且将该结构体的内存地址传递到Create 函数。下面的代码创建了一个互斥(mutex)对象并且返回了一个互斥对象的可继承句柄:

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;   // Make the returned handle inheritable.

HANDLE hMutex = CreateMutex(&sa, FALSE, NULL);

 

This code initializes a SECURITY_ATTRIBUTES structure indicating that the object should be created using default security and that the returned handle should be inheritable.

这段代码初始化了一个 SECURITY_ATTRIBUTES结构体,该结构体决定了该对象是按照默认安全规则创建的,并且创建函数返回的句柄是可继承的。

 

Now we come to the flags that are stored in a process' handle table entry. Each handle table entry has a flag bit indicating whether the handle is inheritable. If you pass NULL as the PSECURITY_ATTRIBUTES parameter when you create a kernel object, the handle returned is not inheritable and this bit is zero. Setting the bInheritHandle member to TRUE causes this flag bit to be set to 1.

 

现在,我们再看看进程对象句柄表各记录里的标识。每个句柄数据表记录拥有一个标识,该标识决定了是否该句柄是可继承的。当你创建一个内核对象时,如果你给PSECURITY_ATTRIBUTES参数传递了NULL值,则该对象句柄是不可继承的,并且句柄表里标识值为0.

将bInheritHandle成员置为TRUE时,将导致对象句柄表里的标识被置为1.

 

Imagine a process' handle table that looks like the one shown in Table 3-2.

想象进程句柄表看起来像下面这个样子:

Index

Pointer to Kernel Object Memory Block内核对象的内存地址

Access Mask (DWORD of Flag Bits)

Flags

1

0xF0000000

0x????????

0x00000000

2

0x00000000

(N/A)

(N/A)

3

0xF0000010

0x????????

0x00000001

Table 3-2 indicates that this process has access to two kernel objects (handles 1 and 3). Handle 1 is not inheritable, and handle 3 is inheritable.

 

此表包含两个有效记录,13.1不能被继承,而3是可以被继承的。

 

The next step to perform when using object handle inheritance is for the parent process to spawn the child process. This is done using the CreateProcess function:

下一步,演示因为父进程产生子进程而使用对象句柄继承的情况,你可使用CreateProcess函数进行:

BOOL CreateProcess(
   PCTSTR pszApplicationName,
   PTSTR pszCommandLine,
   PSECURITY_ATTRIBUTES psaProcess,
   PSECURITY_ATTRIBUTES psaThread,
   BOOL bInheritHandles,
   DWORD dwCreationFlags,
   PVOID pvEnvironment,
   PCTSTR pszCurrentDirectory,
   LPSTARTUPINFO pStartupInfo,
   PPROCESS_INFORMATION pProcessInformation);

 

 

We'll examine this function in detail in the next chapter, but for now I want to draw your attention to the bInheritHandles parameter. Usually, when you spawn a process, you pass FALSE for this parameter. This value tells the system that you do not want the child process to inherit the inheritable handles that are in the parent process' handle table.

关于这个函数我们将在下一章详细讨论,首先,你需要特别注意bInheritHandles参数。通常,当父进程创建一个子进程时,如果你给该标志传值为FALSE,则系统将知道你不想让子进程继承父进程内核对象句柄,该句柄存在于一个父进程的句柄表中。

If you pass TRUE for this parameter, however, the child inherits the parent's inheritable handle values.

但是,如果给该值传递TRUE,子进程将继承父进程的可继承句柄值。

When you pass TRUE, the operating system creates the new child process but does not allow the child process to begin executing its code right away.

当你给该值传递TRUE时,操作系统将创建一个新的子进程但是不会允许该子进程马上执行它自己的代码。

 Of course, the system creates a new, empty process handle table for the child process—just as it would for any new process.

 当然,就像系统给任何进程创建句柄表一样,系统也将给子进程新新建空的进程句柄表。

But because you passed TRUE to CreateProcess' bInheritHandles parameter, the system does one more thing: it walks the parent process' handle table, and for each entry it finds that contains a valid inheritable handle, the system copies the entry exactly into the child process' handle table.

因为你给CreateProcessbInheritHandles参数赋值为TRUE,系统会进行以下操作,首先遍历父进程的句柄表,找到每一项可继承的句柄,然后将其完整的拷贝到子进程的进程句柄表。

 The entry is copied to the exact same position in the child process' handle table as in the parent's handle table.

句柄将被精确的拷贝到和父进程相同的句柄表位置。(句柄在子进程句柄表中的位置和父进程的完全相同。)

 This fact is important because it means that the handle value that identifies a kernel object is identical in both the parent and child processes.

这是个十分重要的事实,它意味着定位内核对象的句柄值在父进程和子进程中都是完全相同的。

In addition to copying the handle table entry, the system increments the usage count of the kernel object because two processes are now using the object.

在句柄被拷贝的同时,内核对象的引用计数被增加了,因为有两个进程现在拥有该对象了。

 For the kernel object to be destroyed, both the parent process and the child process must either call CloseHandle on the object or terminate.

  如果需要销毁内核对象,父进程和子进程都必须在对象之上调用CloseHandle函数或者(terminate函数??)

 The child does not have to terminate first—but neither does the parent.

 In fact, the parent process can close its handle to the object immediately after the CreateProcess function returns without affecting the child's ability to manipulate the object.

子进程和父进程都不是最先结束的。事实上,父进程可以在CreateProcess函数返回时立即关闭对象的句柄,而不影响子进程操作内核对象。(父进程关闭的只是内核对象的句柄,因为在创建子对象时,内核对象的引用计数已经增加了)

 

Table 3-3 shows the child process' handle table immediately before the process is allowed to begin execution. You can see that entries 1 and 2 are not initialized and are therefore invalid handles for the child process to use. However, index 3 does identify a kernel object.

In fact, it identifies the kernel object at address 0xF0000010—the same object as in the parent process' handle table.

Index

Pointer to Kernel Object Memory Block

Access Mask (DWORD of Flag Bits)

Flags

1

0x00000000

(N/A)

(N/A)

2

0x00000000

(N/A)

(N/A)

3

0xF0000010

0x????????

0x00000001

Table 3-3: A Child Process' Handle Table After Inheriting the Parent Process' Inheritable Handle

Open table as spreadsheet

As you will see in Chapter 13, "Windows Memory Architecture," the content of kernel objects is stored in the kernel address space that is shared by all processes running on the system.

For 32-bit systems, this is in memory between the following memory addresses: 0x80000000 and 0xFFFFFFFF. For 64-bit systems, this is in memory between the following memory addresses: 0x00000400'00000000 and 0xFFFFFFF'FFFFFFFF.(在64位系统中,内存地址有点奇怪,因为64位系统操作系统一般都不是64位寻址的,难道我又记错了!???)

 The access mask is identical to the mask in the parent, and the flags are also identical. 存取掩码也是一模一样的(这个是干什么用的???)

This means that if the child process were to spawn its own child process (a grandchild process of the parent) with the same bInheritHandles parameter of CreateProcess set to TRUE, this grandchild process would also inherit this kernel object handle with the same handle value, same access, and same flags, and the usage count on the object would again be incremented.(有点罗嗦,就是子子孙孙无穷尽也,引用计数++

 

Be aware that object handle inheritance applies only at the time the child process is spawned. If the parent process were to create any new kernel objects with inheritable handles, an already-running child process would not inherit these new handles.

可继承的内核对象只能在子进程出生时继承,一个正在运行的子进程不会拥有父进程新创建的可继承内核对象句柄。(父亲在孩子出生后进化成了新的形态,此时孩子也只能拥有它出生时父亲的形态)

 

Object handle inheritance has one very strange characteristic: when you use it, the child has no idea that it has inherited any handles.

对象句柄继承有一个非常奇怪的特性:当你使用子进程时它不知道自己继承了任何句柄。(确实挺奇怪,我不会翻译了!)

Kernel object handle inheritance is useful only when the child process documents the fact that it expects to be given access to a kernel object when spawned from another process.

内核对象句柄继承机制只有在子进程为了证明一个事实时有用,该事实是什么了?是它希望在出生时被赋予操作内核对象的能力。(有点别扭,等完全理解后重新翻译)

Usually, the parent and child applications are written by the same company;

 however, a different company can write the child application if that company documents what the child application expects.

通常,父子应用是同一个团队编写的;(本来以为是公司,后来发现团队可能更贴切)

但是,如果这个团队的应用说明文档写明希望那些子应用由其他团队完成,则另外的团队可以编写这些子应用。(意思很明白,说的很罗嗦)

 

By far, the most common way for a child process to determine the handle value of the kernel object that it's expecting is to have the handle value passed as a command-line argument to the child process.

到目前为止,子进程获取内核对象句柄值的常用方法,是把句柄值当作命令行参数进行传递。(这句比较复杂,不知道如何翻译?)

The child process' initialization code parses the command line (usually by calling _stscanf_s) and extracts the handle value.

子进程初始化代码获取命令行参数(通常使用  _stscanf_s函数)并且从中提取句柄值。

 Once the child has the handle value, it has the same access to the object as its parent.

Note that the only reason handle inheritance works is because the handle value of the shared kernel object is identical in both the parent process and the child process.

一旦子进程获得了句柄值,它就可以和父进程拥有相同的对内核对象的存取权限。

 This is why the parent process is able to pass the handle value as a command-line argument.

Of course, you can use other forms of interprocess communication to transfer an inherited kernel object handle value from the parent process into the child process.

One technique is for the parent to wait for the child to complete initialization (using the WaitForInputIdle function discussed in Chapter 9, "Thread Synchronization with Kernel Objects");

then the parent can send or post a message to a window created by a thread in the child process.

Another technique is for the parent process to add an environment variable to its environment block.

The variable's name would be something that the child process knows to look for, and the variable's value would be the handle value of the kernel object to be inherited.(全局环境变量的方法)

 Then when the parent spawns the child process, the child process inherits the parent's environment variables and can easily call GetEnvironmentVariable to obtain the inherited object's handle value.

This approach is excellent if the child process is going to spawn another child process, because the environment variables can be inherited again.

这个方法很好,因为环境变量是可以再次被继承的。(好的原因,是不是因为节省了内存?)

The special case of a child process inheriting its parent console is detailed in the Microsoft Knowledge Base at http://support.microsoft.com/kb/190351.

 

Changing a Handle's Flags

Occasionally, you might encounter a situation in which a parent process creates a kernel object retrieving an inheritable handle and then spawns two child processes. The parent process wants only one child to inherit the kernel object handle. In other words, you might at times want to control which child processes inherit kernel object handles. To alter the inheritance flag of a kernel object handle, you can call the SetHandleInformation function:

BOOL SetHandleInformation(
   HANDLE hObject,
   DWORD dwMask,
   DWORD dwFlags);

As you can see, this function takes three parameters. The first, hObject, identifies a valid handle. The second parameter, dwMask, tells the function which flag or flags you want to change. Currently, two flags are associated with each handle:

#define HANDLE_FLAG_INHERIT            0x00000001
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002

You can perform a bitwise OR on both of these flags together if you want to change each object's flags simultaneously. SetHandleInformation's third parameter, dwFlags, indicates what you want to set the flags to. For example, to turn on the inheritance flag for a kernel object handle, do the following:

SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);

To turn off this flag, do this:

SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, 0);

The HANDLE_FLAG_PROTECT_FROM_CLOSE flag tells the system that this handle should not be allowed to be closed:

SetHandleInformation(hObj, HANDLE_FLAG_PROTECT_FROM_CLOSE,
   HANDLE_FLAG_PROTECT_FROM_CLOSE);
CloseHandle(hObj);   // Exception is raised

When running under a debugger, if a thread attempts to close a protected handle, CloseHandle raises an exception. Outside the control of a debugger, CloseHandle simply returns FALSE. You rarely want to protect a handle from being closed. However, this flag might be useful if you had a process that spawned a child that in turn spawned a grandchild process. The parent process might be expecting the grandchild to inherit the object handle given to the immediate child. It is possible, however, that the immediate child might close the handle before spawning the grandchild. If this were to happen, the parent might not be able to communicate with the grandchild because the grandchild did not inherit the kernel object. By marking the handle as "protected from close," the grandchild has a better chance to inherit a handle to a valid and live object.

This approach has one flaw, however. The immediate child process might call the following code to turn off the HANDLE_FLAG_PROTECT_FROM_CLOSE flag and then close the handle:

SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);
CloseHandle(hObj);

The parent process is gambling that the child process will not execute this code. Of course, the parent is also gambling that the child process will spawn the grandchild, so this bet is not that risky.

For the sake of completeness, I'll also mention the GetHandleInformation function:

BOOL GetHandleInformation(
   HANDLE hObject,
   PDWORD pdwFlags);

This function returns the current flag settings for the specified handle in the DWORD pointed to by pdwFlags. To see if a handle is inheritable, do the following:

DWORD dwFlags;
GetHandleInformation(hObj, &dwFlags);
BOOL fHandleIsInheritable = (0 != (dwFlags & HANDLE_FLAG_INHERIT));

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值