Windows對象管理
對象管理是Windows Executive的一個子系統實現,用於管理Windows資源。 資源包括物理設備、文件、文件目錄、註冊表條目、正在運行的進程等等。所有子系統訪問資源都必須通過對象管理子系統。
體系結構
Windows NT操作系統體系結構中,對象管理子系統管理所有的資源。資源被表示為對象。對象管理子系統保持着對每個對象的引用計數。任何訪問對象的系統調用都必須通過對象管理子系統。Windows對象可分為內核對象、用戶對象、GDI對象:
- 用戶對象(User interface object):支持窗口管理。每個對象僅有一個句柄,句柄不能複製或繼承,不能引用其他用戶會話中的進程的用戶句柄。任何進程只要有對某個用戶句柄的安全訪問權限,即可以訪問該用戶對象,即用戶對象在當前會話下是全局的。一個進程最多有 65536 個用戶對象句柄。用戶對象包括:快捷鍵表HACCEL、插入點HCaret、鼠標指針HCURSOR、DDE 會話、窗口鈎子HOOK、圖標 HICON、菜單 HMENU、窗口 HWND、窗口位置Window position。[1]
- GDI 對象:支持圖形。每個對象僅有一個句柄,句柄為進程私有。一個進程最多有 65536 個 GDI 對象句柄。GDI 對象包括:位圖 HBITMAP、畫刷 HBRUSH、設備環境HDC、增強型圖元文件(EMF)、EMF 設備環境、字體 HFONT、內存 DC、圖元文件Metafile、圖元文件 DC、調色板 HPALETTE、畫筆 HPEN、區域(Region)HRGN。[2]
- 內核對象:支持內存管理、進程執行、進程間通信。內核對象句柄是進程私有的,必須創建或者打開內核對象以獲取其句柄。當進程創建或打開內核對象時,進程的句柄表中增加一個條目指向內核對象實例。進程的句柄表的索引稱為句柄(handle)[3]。對象管理子系統使用句柄與命名兩種方式管理對象實例。句柄在一個進程內部是線程共享的,但在進程之間不是直接可復用,需要特別方式在進程間傳遞對象句柄。一個進程任何時刻最多擁有 224,即大約 16,000,000 個句柄。句柄按照對象的分類可分為文件句柄、事件句柄、進程句柄等。一個進程對一個對象可以有多個句柄,以便按照不同權限來訪問對象。
對象可分為內核對象(Kernel objects)與執行對象(Executive objects)。內核對象表示一些基本資源,如物理設備、同步服務等等。用戶態的程序不能訪問內核對象。[4]用戶態的系統服務與應用程序使用執行對象,這是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\"前綴來在客戶會話命名空間中創建對象。[5]
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
參加
參考文獻
- Russinovich, Mark; David Solomon. Chapter 3: System Mechanisms. Microsoft Windows Internals 4th edition. Microsoft Press. 2005: 124–149. ISBN 0-7356-1917-4.
- ^ User Objects. [2014-01-08]. (原始內容存檔於2017-09-18).
- ^ GDI Objects. [2014-01-08]. (原始內容存檔於2017-09-18).
- ^ 每個進程中都存在一個句柄表,列出了所有本進程內可以使用的句柄。句柄表實際上是一個數組,每個數組元素為一個結構,包含一個指向內核對象的指針、訪問掩碼、繼承標識等。句柄實際上是進程句柄表數組的索引。因此句柄是進程私有的。進程的句柄表的表頭數據結構為HANDLE_TABLE。所有進程的句柄表表頭形成一個List。句柄表的表項的數據結構為HANDLE_TABLE_ENTRY,長度為8字節,其中前四個數據為內核對象地址,後四個字節為訪問掩碼或在當前表項為空閒時存儲下一空閒表項的索引值。
- ^ Kernel objects. [2014-01-08]. (原始內容存檔於2017-09-18).
- ^ Kernel object namespaces