第3章 内核对象(1)

3.1 何为内核对象

3.1.1 Windows平台上的3大对象

(1)分类

对象

描述

备注

GUI对象

也叫用户对象,一般是单线程访问,属于线程级的对象,如

加速键表(HACCEL)、插入记号(Caret)、光标(HCURSOR)、桌面(HDESK)、

钩子(HHOOK)、图标(HICON)、菜单(HMENU)、窗口(HWND)、窗口栈(HWINSTA)

句柄值是系统唯一的,即一个进程可以通过该句柄值对另一个进程中的对象进行操作,如发送消息。

GDI对象

如DC、Pen、Font等,一般是单线程访问,属于线程级的对象

句柄值只在进程内有效

内核对象

如进程、线程、文件等,一般允许多进程、多线程访问,因为内核对象有严格的安全机制及多线程访问控制策略,属系统级的对象。

句柄值是表示该内核对象在句柄表里的一个索引(注意,只是个索引值),该句柄值是与进程相关的,同一个对象在不同进程中可能有不同的索引

(2)内核对象——所有者为操作系统内核,而非进程

内核对象

对象名称

句柄

类型

创建函数

销毁函数

Access token

访问令牌

HANDLE

CreateRestrictedToken, DuplicateToken(Ex),

OpenProcessToken,

OpenThreadToken

CloseHandle

Change notification

文件、目录变更通知

HANDLE

FindFirstChangeNotification

FindCloseChangeNotification

Communications

device

串口通信

HANDLE

CreateFile

CloseHandle

Console input

控制台输入

HANDLE

CreateFile,with CONIN$

CloseHandle

Console screen

buffer

控制台输出

HANDLE

CreateFile,with CONOUT$

CloseHandle

Desktop

桌面

HDESK

GetThreadDestop

应用程序无法删除该对象

Event

事件

HANDLE

CreateEvent, CreateEventEx, OpenEvent

CloseHandle

Event log

事件日志

HANDLE

OpenEventLog, RegisterEventSource, OpenBackupEventLog

CloseEventLog

File

文件

HANDLE

CreateFile

CloseHandle, DeleteFile

File mapping

文件映射

HANDLE

CreateFileMapping, OpenFileMapping

CloseHandle

Find file

文件查找

HANDLE

FindFirstFile

FindClose

Heap

HANDLE

HeapCreate

HeapDestroy

I/O completion port

完成端口

HANDLE

CreateIoCompletionPort

CloseHandle

Job

作业

HANDLE

CreateJobObject

CloseHandle

Mailslot

邮槽

HANDLE

CreateMailslot

CloseHandle

Memory resource notification

内存资源通知

HANDLE

CreateMemoryResourceNotification

CloseHandle

Module

模块

HMODULE

LoadLibrary, GetModuleHandle

FreeLibrary

Mutex

互斥

HANDLE

CreateMutex, CreateMutexEx, OpenMutex

CloseHandle

Pipe

管道(命名、匿名)

HANDLE

CreateNamedPipe, CreatePipe

CloseHandle, DisconnectNamedPipe

Process

进程

HANDLE

CreateProcess, OpenProcess, GetCurrentProcess

CloseHandle, TerminateProcess

Semaphore

信标量

HANDLE

CreateSemaphore, CreateSemaphoreEx, OpenSemaphore

CloseHandle

Socket

套接字

SOCKET

socket, accept,WSASocket

closesocket

Thread

线程

HANDLE

CreateThread, CreateRemoteThread, GetCurrentThread

CloseHandle, TerminateThread

Timer

计时器

HANDLE

CreateWaitableTimer, CreateWaitableTimerEx, OpenWaitableTimer

CloseHandle

Update resource

更新资源

HANDLE

BeginUpdateResource

EndUpdateResource

Window station

窗口栈

HWINSTA

GetProcessWindowStation,CreateWindowStation

CloseWindowStation

3.1.2 内核对象的安全性

(1)安全描述符SD(SECURITY_DESCRIPTOR)

字段

描述

UCHAR  Revision

 

UCHAR  Sbz1

 

SECURITY_DESCRIPTOR_CONTROL

 Control;

一个控制位集合,说明安全描述符的含义或它每个成员

PSID  Owner

拥有者的安全ID

PSID  Group

基本组对象SID

PACL  Sacl

系统访问控制链表,当一个进程常识访问一个安全对象的时候,系统检查对象的 DACL 中的访问控制实体来决定是否赋予访问权限。

PACL  Dacl

DACL指定特殊用户或组的允许或拒绝的访问权限

★程序不需要直接操作安全描述符的内容。Windows API提供设置和返回安全描述符号的函数。另外,有用来创建和初始化一个新对象安全描述符号的函数。

(2)SECURITY_ATTRIBUTES结构体——创建内核对象几乎都要传此参数。

字段

描述

LPVOID nLength

结构体大小

lpSecurityDescriptor

指向安全描述符SD的指针

BOOL bInheritHandle;

是否继承父进程可继承的对象句柄

3.2 进程内核对象句柄表

3.2.1 进程句柄表(Handle Table)的结构

(1)进程句柄表仅供内核对象使用,不适用于用户对象或GDI对象

(2)进程初始化时,句柄表为空,当创建一个内核对象时,加入句柄表的记录项。

(3)进程句柄表的结构

索引

指向内核对象内存块的指针

访问掩码(包含标志位的DWORD)

标志

1

0x????????

0x????????

0x????????

2

0x????????

0x????????

0x????????

……

……

……

……

3.2.2 创建一个内核对象

(1)创建内核对象的函数:如CreateThread、CreateFile、CreateSemaphore等。(在32位系统中,内核对象的内容被保存在0x80000000至0xFFFFFFFF的这个内核地址空间中)

(2)返回值,是个句柄,表示该内核对象的在句柄表中的索引。(注意因句柄表可能会分层,所以该句柄最后两位(共32位)表示该对象在句柄表中所在的层数,因此如果要得到实际的索引值,必将该句柄值右移2位,见《Windows内核原理与实现·潘爱民》,p131图3.4)

(3)创建失败时,返回0(NULL),这也是句柄表中第1个内核对象的索引值为1,而为是0的原因。(注意句柄值是实际的索引值的4倍,所以第1个对象的返回的句柄值是4)。但有些函数的返回值是-1(INVALID_HANDLE_VALUE),如CreateFile,使用时请参考MSDN说明。

(4)调用一个内核对象时,在该函数内部会查找进程的句柄表,获得目标内核对象地址来操作对象的数据结构,如果传入的是一人无效句柄,调用失败。GetLastError将返回ERROR_INVALID_HANDLE。

3.2.3 关闭内核对象

(1)CloseHandle内部发生的事情

  ①首先检查进程的句柄表,验证传进来的函数该句柄的访问权限

②如果句柄有效,获得该内核对象的内存地址,并将该内核对象的“使用计数”减1

③如果计数变为0,将内核对象销毁。

(2)返回值:

成功

TRUE

失败

当进程是正常运行的,返回FALSE;

当进程正在被调试,返回EEEOR_INVALID_HANDLE,便于调试错误。

(3)CloseHandle的几点说明

  ①内核对象的生命期可能长于进程的生命期。CloseHandle只是表明这个句柄不再使用了,但内核对象并不一定被销毁(可能被其他线程引用)。无论内核对象是否被销毁,都不要再试图用这个值。当 CloseHanle后,一般要将该值设为NULL。

②如果不小心再次使用这个句柄值来调用内核对象,则将可能发生如下意外情况:

  A、当句柄表中的记录项己被完全删除时,会收到一个无效参数的错误报告。

B、当句柄表中的记录项被删除后,该项被新加入的内核对象重新填补,如果新内核对象与旧的内核对象类型不同,则会定位到该对象,但会报错。如果类型相同,这里并不会报错,但应用程序可能出现难于预料的结果,甚至应用程序状态被损坏,而无法恢复。

C、如果该记录未被删除(即,使用计数不为0,说明有其他线程调用),则会继续定位到该对象,但这很不安全。因为我们己经调用CloseHanle了,所以该对象不再受我们的控制,它将在什么时候被销毁,我们并不知道。这时如果这时另一线程也调用CloseHandle,则会出现内核对象被销毁,我们就会引用了一个被销毁的对象,导致程序出错。

④如果忘记调用CloseHandle,在进程运行期间会导致内存泄漏,但程序结束后,该对象仍会被系统正确清除

原文链接: https://www.cnblogs.com/5iedu/p/4649809.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第3章 内核对象(1)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/394829

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:55
下一篇 2023年4月3日 下午3:55

相关推荐