第一部分,必备知识
第3章 内核对象
概述: 内核对象是Windows操作系统用来管理进程、线程、文件等系统资源的数据结构。它们由内核分配和拥有,通过句柄访问,具有安全描述符、使用计数等特性,是理解Windows编程的核心概念。
什么是内核对象
内核对象是操作系统内核分配的一块内存,由内核拥有和管理,而不是由应用程序拥有。每个内核对象都有一个安全描述符(描述谁可以访问该对象)和一个使用计数(记录有多少个进程正在使用该对象)。常见的内核对象包括进程、线程、文件、事件、信号量、互斥量等。应用程序无法直接访问内核对象的内存,只能通过Windows提供的API和返回的句柄来操作这些对象。
HANDLE CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName );
HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );
|
内核对象的使用计数和生命周期
内核对象的生命周期由使用计数(usage count)控制。当创建一个内核对象时,使用计数被设为1。当另一个进程获得该对象的访问权时,使用计数递增;当进程不再需要该对象时,应调用CloseHandle递减使用计数。当使用计数变为0时,内核会销毁该对象并释放内存。即使创建该对象的进程终止,只要其他进程还在使用该对象,对象就不会被销毁。这种机制确保了内核对象可以在进程间共享。
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (hEvent == NULL) { DWORD dwError = GetLastError(); }
HANDLE hEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("MyEvent")); if (hEvent2 != NULL) { }
BOOL CloseHandle(HANDLE hObject);
if (!CloseHandle(hEvent)) { DWORD dwError = GetLastError(); }
hEvent = NULL;
|
内核对象的安全性
每个内核对象都有一个安全描述符(SECURITY_ATTRIBUTES结构),用于指定谁可以访问该对象以及拥有什么权限。安全描述符包含所有者的SID(安全标识符)、DACL(自主访问控制列表,指定允许或拒绝哪些用户的访问)和SACL(系统访问控制列表,用于审计)。创建内核对象时可以通过lpSecurityAttributes参数指定安全属性,传入NULL则使用默认安全属性(仅创建者拥有完全访问权限)。
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE;
HANDLE hEvent = CreateEvent(&sa, FALSE, FALSE, TEXT("SecureEvent"));
|
跨进程共享内核对象
Windows提供了三种机制在不同进程间共享内核对象:句柄继承、命名对象和复制句柄。句柄继承允许子进程继承父进程的句柄表中的可继承句柄。命名对象通过给对象指定全局唯一的名称,其他进程可以通过OpenXxx函数按名称打开。复制句柄则使用DuplicateHandle函数将一个进程的句柄复制到另一个进程。这些方法使得进程间可以安全地共享和同步资源。
SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE;
HANDLE hEvent = CreateEvent(&sa, FALSE, FALSE, NULL);
STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess( TEXT("Child.exe"), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi );
HANDLE hInheritedEvent = (HANDLE)atoi(argv[1]);
HANDLE hNamedEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("Global\\MyEvent"));
HANDLE hNamedEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("Global\\MyEvent"));
BOOL DuplicateHandle( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions );
HANDLE hProcessB = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdB); HANDLE hEventInProcessB; DuplicateHandle( GetCurrentProcess(), hEvent, hProcessB, &hEventInProcessB, 0, FALSE, DUPLICATE_SAME_ACCESS );
|
总结:
W内核对象是Windows操作系统管理资源的核心机制,由内核拥有并通过句柄访问。每个内核对象都有安全描述符和使用计数,确保安全和正确的生命周期管理。使用内核对象时必须通过CloseHandle关闭句柄以递减使用计数,避免资源泄漏。进程间可以通过句柄继承、命名对象和DuplicateHandle函数共享内核对象。理解内核对象是后续学习进程、线程、同步等高级主题的基础。