# Windows对象管理[编辑]

**对象管理**是Windows Executive的一个子系统实现，用于管理Windows资源。 资源包括物理设备、文件、文件目录、注册表条目、正在运行的进程等等。所有子系统访问资源都必须通过对象管理子系统。

## 体系结构

Windows NT操作系统体系结构中，对象管理子系统管理所有的资源。资源被表示为对象。对象管理子系统保持着对每个对象的引用计数。任何访问对象的系统调用都必须通过对象管理子系统。Windows对象可分为内核对象、用户对象、GDI对象：

* 用户对象（User interface object）：支持窗口管理。每个对象仅有一个句柄，句柄不能复制或继承，不能引用其他用户会话中的进程的用户句柄。任何进程只要有对某个用户句柄的安全访问权限，即可以访问该用户对象，即用户对象在当前会话下是全局的。一个进程最多有 65536 个用户对象句柄。用户对象包括：快捷键表HACCEL、插入点HCaret、鼠标指针HCURSOR、DDE 会话、窗口钩子HOOK、图标 HICON、菜单 HMENU、窗口 HWND、窗口位置Window position。
* GDI 对象：支持图形。每个对象仅有一个句柄，句柄为进程私有。一个进程最多有 65536 个 GDI 对象句柄。GDI 对象包括：位图 HBITMAP、画刷 HBRUSH、设备环境HDC、增强型图元文件（EMF）、EMF 设备环境、字体 HFONT、内存 DC、图元文件Metafile、图元文件 DC、调色板 HPALETTE、画笔 HPEN、区域（Region）HRGN。
* 内核对象：支持内存管理、进程执行、进程间通信。内核对象句柄是进程私有的，必须创建或者打开内核对象以获取其句柄。当进程创建或打开内核对象时，进程的句柄表中增加一个条目指向内核对象实例。进程的句柄表的索引称为**句柄**（handle）。对象管理子系统使用句柄与命名两种方式管理对象实例。句柄在一个进程内部是线程共享的，但在进程之间不是直接可复用，需要特别方式在进程间传递对象句柄。一个进程任何时刻最多拥有 224，即大约 16,000,000 个句柄。句柄按照对象的分类可分为文件句柄、事件句柄、进程句柄等。一个进程对一个对象可以有多个句柄，以便按照不同权限来访问对象。

对象可分为**内核对象**（Kernel objects）与**执行对象**（Executive objects）。内核对象表示一些基本资源，如物理设备、同步服务等等。用户态的程序不能访问内核对象。用户态的系统服务与应用程序使用执行对象，这是Windows Executive对外暴露的对象，用来封装一个或多个内核对象。执行对象还用于实现 NT 子系统或 POSIX 子系统的一些功能。

Windows NT 暴露的执行对象包括:

| 类别 | 描述 | 创建 / 获取句柄系统调用 | 创建 / 获取句柄函数 | 释放句柄函数 | 未通知状态 | 通知状态 | 等待成功的副作用
 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 目录
 | 用来存放内核对象。多级嵌套的目录将所有内核对象组织成一个树形结构
 | NtCreateDirectoryObject NtOpenDirectoryObject
 |  |  |  |  | 无
 |
| 进程
 | 线程的集合，拥有共同的虚拟内存空间与控制信息
 | NtCreateProcess NtOpenProcess
 | CreateProcess OpenProcess GetCurrentProcess
 | CloseHandle TerminateProcess
 | 进程仍然活动时
 | 进程终止运行时 （TerminateProcess ExitProcess）
 | 无
 |
| 线程
 | 进程内部，执行程序的实体。
 | NtCreateThread NtOpenThread
 | CreateThread CreateThreadEx OpenThread GetCurrentThread
 | CloseHandle TerminateThread
 | 线程仍然活动时
 | 线程终止运行时 （TerminateThread ExitThread）
 | 无
 |
| 作业
 | 进程的集合
 | NtCreateJobObject NtOpenJobObject
 | CreateJobObject
 | CloseHandle
 | 当作业的时间尚未结束时
 | 当作业的时间已经结束时
 | 无
 |
| 文件
 | 一个打开的计算机文件或 I/O 设备。
 | NtCreateFile NtOpenFile
 | CreateFile
 | CloseHandle DeleteFile
 | 当 I/O 请求正在处理时
 | 当 I/O 请求处理完毕时
 | 无
 |
| 文件映射对象
 | 一块内存区域，映射到一个文件。
 | NtCreateSection NtOpenSection
 | CreateFileMapping
 | CloseHandle
 |  |  |  |
| 访问令牌
 | 一个对象的访问权。
 | NtCreateToken NtDuplicateToken NtOpenProcessToken NtOpenThreadToken
 | CreateRestrictedToken DuplicateToken DuplicateTokenEx OpenProcessToken OpenThreadToken
 | CloseHandle
 |  |  |  |
| 事件
 | 封装了某些信息的一个对象，用于通知某些进程。
 | NtCreateEvent NtOpenEvent
 | CreateEvent CreateEventEx OpenEvent
 | CloseHandle
 | ResetEvent，或 PulseEvent，或自动重置事件等待成功
 | 当调用 SetEvent，或 PulseEvent
 | 自动重置事件等待成功后将自动重置
 |
| 信号量
 | 用于串行化访问某些资源的对象。
 | NtCreateSemaphore NtOpenSemaphore
 | CreateSemaphore CreateSemaphoreEx OpenSemaphore
 | CloseHandle
 | 当数量 <=0 时
 | 当数量 >0 时（ReleaseSemaphore）
 | 数量减 1
 |
| 互斥锁
 | 用于串行化访问某些资源的对象。
 | 无
 | CreateMutex CreateMutexEx OpenMutex
 | CloseHandle
 | 被其他线程拥有时
 | 未被其他线程拥有时
 | 等待成功的线程获得 CPU 所有权
 |
| 临界区域
 | 使得指定的代码段被串行执行
 | 无
 | InitializeCriticalSection InitializeCriticalSectionAndSpinCount
 | DeleteCriticalSection
 | 被其他线程拥有时（试图 EnterCriticalSection）
 | 未被其他线程拥有时（LeaveCriticalSection）
 | 等待成功的线程获得 CPU 所有权
 |
| 定时器
 | 按照固定时间间隔通知某些进程的对象。
 | NtCreateTimer NtOpenTimer
 | CreateWaitableTimer CreateWaitableTimerEx
 | CloseHandle
 | CancelWaitableTimer 或自动重置定时器等待成功
 | 当时间到时（SetWaitableTimer）
 | 自动重置定时器等待成功将 reset
 |
| Timer queue
 |  | 无
 |  |  |  |  |  |
| Timer-queue timer
 |  | 无
 |  |  |  |  |  |
| 注册表键值
 | Windows 注册表条目的键值，数据类型不是 HANDLE 而是 HKEY
 |  | RegCreateKeyEx RegOpenKeyEx
 | RegCloseKey
 |  |  |  |
| 桌面
 | 包含 GUI 元素的一个逻辑显示面，数据类型是 HDESK
 | 无
 | CreateDesktop OpenDesktop GetThreadDesktop
 | CloseDesktop
 |  |  |  |
| WindowStation
 | 包含一些桌面对象、一块剪贴板对象、以及其他对象的对象，数据类型是 HWINSTA
 | 无
 | CreateWindowStation OpenWindowStation GetProcessWindowStation
 | CloseWindowStation
 |  |  |  |
| 剪贴板
 | 用于其它对象的临时存储空间。
 | 无
 | OpenClipboard
 | CloseClipboard
 |  |  |  |
| 符号链接
 | 对其他对象的引用
 | NtCreateSymbolicLinkObject NtOpenSymbolicLinkObject
 |  |  |  |  |  |
| 事件日志
 |  | 无
 | OpenEventLog RegisterEventSource OpenBackupEventLog
 | CloseEventLog
 |  |  |  |
| 目录下更改通知
 | 若指定的目录下发生了文件名、属性等更改，则唤起本事件
 |  | FindFirstChangeNotification
 | FindCloseChangeNotification
 | 没有发生指定监视类型的修改
 | 发生了指定监视类型的修改
 | 重置事件状态；若需要监视下一次更改，使用循环搭配 FindNextChangeNotification
 |
| 堆内存
 |  | 无
 | HeapCreate GetProcessHeap GetProcessHeaps
 | HeapDestroy 不应释放进程的缺省堆
 |  |  |  |
| I/O 完成端口
 |  | NtCreateIoCompletion NtOpenIoCompletion
 | CreateIoCompletionPort
 | CloseHandle
 |  |  |  |
| Mailslot
 |  | NtCreateMailslotFile
 | CreateMailslot
 | CloseHandle
 |  |  |  |
| 内存资源通知
 |  |  | CreateMemoryResourceNotification
 | CloseHandle
 |  |  |  |
| 模块
 | 只有自己 LoadLibrary 加载的 DLL 才需要释放；数据类型为 HMODULE；WinMain 函数的 hInstance 参数是指向 EXE 主程序的 HMODULE（数据类型为 HINSTANCE）
 | 无
 | LoadLibrary LoadLibraryEx GetModuleHandle GetModuleHandleEx WinMain 函数的 hInstance 参数
 | FreeLibrary
 |  |  |  |
| 管道
 | 管道可以是双工（双向传输）的；创建管道的一方称为服务端，连接到已存在或将要存在的管道的一方称为客户端
 | NtCreateNamedPipeFile
 | CreateNamedPipe CreatePipe CreateFile
 | CloseHandle DisconnectNamedPipe
 |  |  |  |
| 套接字
 | Unix 下的套接字只是普通 fd，而 Windows 下的套接字一般而言和普通（文件系统）文件不一样，推荐使用专门的套接字 API 操作；数据类型是 SOCKET（UINT\_PTR）
 | 无
 | WSASocket WSAAccept
 | closesocket
 |  |  | 与文件类似
 |
| 资源更新
 | 使用 UpdateResource 更新某个 PE 模块中的资源（Resource，包括字符串、图标等）
 | 无
 | BeginUpdateResource
 | EndUpdateResource
 |  |  |  |

### 对象结构

每个被对象管理子系统所管理的对象，包含头部和体部。头部是对象管理子系统使用的状态信息。体部是对象相关的数据与暴露的服务。

对象头部对外暴露的信息称为`Properties`, 包括：

* `Object Name`，用来标识对象
* `Object Directory`，对象所属类别
* `Security Descriptors`，对象的访问权限，一般在创建对象时传入，大多数时候传入值为NULL，表示采用默认安全属性。
* `Quota Charges`，对象的资源使用信息
* `Open handle count`，打开的句柄计数
* `Open handle list`，活动引用的进程列表
* `Reference count`，活动引用进程的计数
* `Type`，用来标识对象体部结构

对象管理子系统所管理的对象必须提供下属服务:

* `Close`，关闭对象的一个句柄
* `Duplicate`，创建对象的另一个句柄，用来给另一个进程共享访问该对象
* `Query object`，获得对象的属性与性质等信息
* `Query security`，得到对象的安全描述信息
* `Set security`，改变对象的安全访问信息
* `Wait`，同步一个或多个对象，通过特定事件。

同一类型的对象具有一些相同的属性，如类型名、是否分配在非分页内存、访问权限、同步信息等。这些由一个类型对象（type object）来表示。所有同一类型的对象实例共享这唯一的类型对象。 可以创建新的对象类型，这通过把一个对象的属性作为对外暴露的状态，把其方法作为对外暴露的服务来实现。

对象名（`Object name`）是一个对象的描述性标识。对象管理子系统保持一个已经用于表示对象的名字列表，映射每个名字到对象实例。实际上大多数访问对象的行为是通过句柄；通过对象名来查找对象实例仅发生在创建对象时、跨进程共享一个对象时。

`Object directories`用于按照类型来分类对象。预定义的`Object directories`包括：

* `\??` （Win32 设备名，其中只有符号链接）
* `\BaseNamedObjects` （互斥、事件、信号量、可等待计时器和段对象）
* `\Callback` （回调函数）
* `\Device` （设备）
* `\Drivers`
* `\FileSystem`
* `\KnownDlls`
* `\Nls` (language tables)
* `\ObjectTypes` （对象类型对象）
* `\RPC Controls` （RPC端口）
* `\Security` （安全子系统对象）
* `\Windows` （窗口子系统对象）

对象属于命名空间（*Namespace*）. 每个用户会话（user session）是一个名字空间。这使得多个客户同时运行一个应用程序而不会发生干扰。在所有名字空间共享的对象属于*GLOBAL*命名空间。例如，在Global命名空间中创建一个事件，名字叫CSAPP：

```
CreateEvent( NULL, FALSE, FALSE, "Global\\CSAPP" );

```

全局命名空间使得多个客户会话间的进程可以通信。例如，一个客户/服务器使用互斥锁来同步，服务器模块在全局命名空间创建一个互斥锁对象，然后客户进程使用"Global\"前缀来打开这个互斥锁对象。 客户进程可以明示使用 "Local\"前缀来在客户会话命名空间中创建对象。

OBJECT\_ATTRIBUTES 结构:

```
 typedef struct \_OBJECT\_ATTRIBUTES{
 ULONG Length;
 HANDLE RootDirectory;
 PUNICODE\_STRING ObjectName;
 ULONG Attributes;
 PSECURITY\_DESCRIPTOR SecurityDescriptor;
 PSECURITY\_QUALITY\_OF\_SERVICE SecurityQualityOfService;
 }OBJECT\_ATTRIBUTES;

```

Attributes 成员域可以是 0，或下述标志的组合：

```
OBJ_INHERIT
OBJ_PERMANANT
OBJ_EXCLUSIVE
OBJ_CASE_INSENSITIVE
OBJ_OPENIF
OBJ_OPENLINK
OBJ_KERNEL_HANDLE

```

## 参加

* Windows NT体系结构

* Russinovich, Mark; David Solomon. Chapter 3: System Mechanisms. Microsoft Windows Internals 4th edition. Microsoft Press. 2005: 124–149. ISBN 0-7356-1917-4.  引文使用过时参数`coauthors` (帮助) 引文格式1维护：冗余文本 (link)

* Object Manager Routines (Windows Drivers)
* Channel9 Interview （页面存档备份，存于互联网档案馆）