计算机成熟思路回顾(三)

网友投稿 552 2022-09-12

计算机成熟思路回顾(三)

计算机成熟思路回顾(三)

这次聊聊关于Windows 内核对象的一些相关内容:

1.什么是内核对象

Kernel Object

还记得在学校图书馆抱着一本操作系统相关的书籍,虽然看不懂,但是觉得介绍的还是挺条理。

有Kernel oBject  就有 Handle(虽然我也想调侃一下这个单词的翻译,简直是奇葩的典范,不知道中文到底有没有这个词)

基本作用:Kernel Object 管理着进程,线程,文件以及诸多种类的资源

常见类型:access token Object,事件对象,文件对象,文件映射对象,I/O完成端口对象

作业对象,mailslot Object,mutex(互斥量)对象,pipe Object,进程对象

semaphore(信号量) Object,线程对象,waitable timer Object,Thread pool woker factory Object...

常用Windows工具包 :SysinternalsSuite

常见Kernel Object 列表截图

不过确实没有啥定义,就是这些Object 就属于Kernel Object

对象创建思路:Kernel Object  通过不同的名称函数创建不同的对应具体某一个的Kernel Object

(注:但是函数名称可能可能不是和内核级别的对象类型对应)

例子:CreateFileMapping,将创建一个Section对象的文件映射

本质:一般对象都是一个内存块,所以内核对象也只是一个内存块。

权限:由操作系统内核分配,并且只能由操作系统内核访问

内容:对象都是一个数据结构,其成员维护着与对象相关的信息,内核对象也是如此。其中一部分成员是所有对象共有的

例子:一个(进程对象)有自己的进程ID,一个基本的优先级,一个推出代码,这些应该就是进程对象特有的

一个文件对象有一个字节偏移量,一个共享模式,一个打开模式

限制:一般的应用程序时不能在内核对象中定位数据结构并且直接修改,因为本身只能由内核本身操作

Micorsoft有意强化这个限制,所以Microsoft就可以自由的添加和删除,修改这些结构的成员,高内聚,不会影响使用的应用程序

操纵:使用Windows提供的一组函数来操作

操作思路:蛤蟆数据范围标识对象的handle,使用这个值就可以继续使用函数对它操作。32位系统,handle是32位值;64位系统是一个64位值

操作限制:handle是和进程相关的;所以handle传给别的进程就会发生调用失败

经典思路:垃圾回收,(计数法,php 也是使用这个思路)

内核对象的所有者是操作系统内核,而非进程。虽然是进程创建了内核对象,进程终止进行,内核对象不一定销毁。

大多数情况下会进入 销毁垃圾回收阶段。

但是如果有别的进程在使用,就不会重复创建,而是使用这一个,复用一下。

所以内核对象的生命周期可能超过创建它的进程。

Usage count

操作系统使用计数器来记录多少个正在使用一个特定的内核对象

该Usage Count 是所有内核对象类型都有的一个数据成员;默认值为1

另一个进程获得该内核对象的访问后,Usage Count会加一

Usage Count变成0,操作系统内核就会销毁该对象。(是的,PHP Zend引擎的垃圾回收就是这样标记对象引用的)

经典的设计思路总是会不自觉的运行在各处。

常规关于内核对象的基本知识,创建,销毁,生命周期就是这些东西。

以下都是相关细节。可能就不是通用的了

****************************************************************************************************************

2.内核对象的安全性

Security Description(SD)

这个变量来保护内核对象

含义:

定义了谁拥有对象,哪些用户和用户被允许访问或使用该对象;

哪些组和用户被拒绝访问此对象。

例子:用于创建内核对象的函数几乎都有一个指向SECURITY_ATTRIBUTES 结构的指针作为参数

Handle CreateFileMapping(

Handle handle,

PSECURITY_ATTRIBUTES psa,

DWORD flProtect,

DWORD dwMaximumSizeHigh,

DWORD  dwMaximumSizeLow,

PCTSTR pszName)

该 psa默认接收一个NULL,代表具有默认的安全性

默认值和当前的security token有关系

当然,也可以分配一个非NULL的SECURITY_ATTRIBUTES结构,

初始化后,将这个变量的地址传给psa

SECUTIRY_ATTRIBUTES结构如下:(C语言的结构体)

typedef  struct _SECURITY_ATTRIBUTES{

DWORD nLength;

LPVOLD lpSecurityDescriptor;

BOOL bInheritHandle;

} SECURITY_ATTRIBUTES

这样来看,只有一个与安全性相关的变量:lpSecurityDescriptor,

初始化例子:

SECURITY_ATTRIBUTES sa;sa.nLength = sizeof(sa);sa.lpSecurityDescriptor = pSD;sa.bInheritHandle = FALSE;Handle MyFileMapping = CreateFileMapping(InValid_HANGLE_VALUE,&sa,PAGE_READWRITE,0,1024,TEXT("HELLO"));

获取已有内核对象的访问:必须指定打算对这个对象执行哪些操作

例子:

HANDLE MyFileMapping = OpenFileMapping(FILE_MAP_READ,FALSE,TEXT("HEllo"));

解释:其中第一个参数就是表明我对这个对象进行读取操作

该函数返回一个handle之前会进行一个安全检查:允许则返回有效handle,否则就是null(调用getLastError返回5(ERROR_ACCESS_DENIED))

第二步,安全检查还会对权限内容判断,是否包括执行的操作,如果没有FILE_MAP_READ,也会拒绝访问

注:忽视正确的安全访问标志是很多开发人员最大的失误之一。

注:只有使用了正确的安全访问标志,我i们的程序就可以容易在不同的windows版本中移植

(苹果就认为开发人员的行为无法控制,所以强制将最佳实践固定化,不给你机会自己操作)

对于优秀程序员,这是一种侮辱和挑衅。但是大部分都是普通用户,这就没啥问题,毕竟产品与系统还是两回事;

可能只是因为开源是一种信仰,对于思想,应该可以自由获取

3:Handle 进程内核对象的handle Table

这里就更加具体化,是为了讨论进程对象的handle Table.

初始化:一个进程在初始化时候,系统会分配一个handle Table

限制:该Handle Table 仅仅供内核对象使用,用户或者GDI是不可以

内容:这一条死胡同!结构如何?如何管理Handle Table,暂时没有文档开源描述

一般情况下该Table 结构如下

索引

指针(内存块地址)

访问掩码(一个DWORD)

标志(地址的一个16进制值)

使用:进程初始化时候,该表一定是空的;

注:当其中的一个线程调用一个创建Kernel Object 的函数时候,就会初始化该对象,

并且会在进程的handle Table 找一个空白位置,然后初始化记录:

指针会设置为对象的内部内存地址,访问掩码会设置为拥有完全访问权限,标志后续再说

例子:

handle CreateThread( PDWORD pdwThreadID ...)handle CreateFile( HANDLE hTemplateFile ...)handle CreateFileMapping( HANDLE hFile ...)handle CreateSemaphore( PSECURITY_ATTRIBUTES psa ...)

从上面来看,创建内核对象的任何函数都会返回一个与进程相关的handle

这个handle 可以由同一个进程中运行的所有线程来使用

注:系统使用索引来标识内核对象的信息保存在进程 Handle Table 的具体位置

想要获得实际的索引值,Handle 实际上来说需要除以4(右移两位)

所以实际值一般都是比较小,4,8啥的,但是这是推测,没有文档

创建内核对象失败后,返回的handle一般是0/null,但是也有INVALID_HANDLE_VALUE

所以这一块不统一,处理需要仔细、

4.关闭内核对象:

CloseHandle,

Bool CloseHandle(HANDLE hobject)

流程:

在内部先检查主调进程的 Handle Table

验证传递给函数的handle  标识的是  进程确实有权访问  的一个对象

如果有效,获得内核对象的内存地址,并且将Usage Count减一

如果无效,进程正常的话,则返回false,(getLastError,返回ERROR_INVALID_HANDLE)

如果进程在调试,则抛出  0x C0000008 异常;

此流程中,会清理掉Handle Table中该记录,所以这个handle一定就不可用了:

不关闭的话,也不一定会发生Memory Leak:

进程终止,操作系统会保证所有该进程使用的资源全部释放,扫描handle Table ,释放所有有效的记录

在进程还在运行,那么这些没有销毁的内存对象,可能会遗留;

如何在应用程序运行的时候检测内核对象遗留。

使用之前的小工具,结合windows任务管理器

5.共享内核对象

主要是跨越进程的共享

为何需要共享:这个思路应该在很多多线程 或者多进程的编程中类似思考

文件映射对象,可以在多个进程和之间共享同一个文件借助pipe ,mailslot可以在进程之间发送消息互斥量,信号量,事件允许在不同进程中的线程同步执行(也就是一个应用在完成某个任务后,就会向另一个应用发消息)

设计考虑:

将内核对象的handle 设计成 进程相关,最重要的原因就是健壮性  安全性

共享的方式:

使用对象句柄集成为对象命名复制对象句柄

第一种:

为了创建一个继承的句柄,

第一步,父进程必须分配并且初始化一个SECURITY_ATTRIBUTES结构

并且将这个地址传给具体的Create函数

SERCURITY_ATTRIBUTES sa;sa.nLength = sizeof(sa);sa.lpSecurityDescription = NULL;sa.hInherithandle = true;handle hMutex = CreateMutex(&sa,False,NULL);

从上面可以看出,就是其中一个hInherithandle 的布尔值设置

下一步:

使用CreateProcess

CreateProcess( PCTSTR pszApplicationName, PTSTR pszCommandLine, PSECURITY_ATTRIBUTES psaProcess, PSECURITY_ATTRIBUTES psaThread, BOOL bInheritHandle, DWORD dwCreationFlags, PVOID pvEnvironment, PCTSTR pszCurrentDirectory, LPSTARTUPINFO pStartupInfo, PROCESS_INFORMATION pProcessInfomation)

从上面可以知道就是 bInheritHandle

如果是TRUE,那么子进程就会继承父进程的值bInheritHandle

创建子进程的时候,会遍历父进程的Handle Table ,对于其中的每一个记录值都会检查,

凡是包含bInheritHandle的项,都会被完整的复制到子进程自己的Handle Table

在子进程的表中,复制项的位置与父进程的一模一样,这是一个非常重要的设计

在父进程和子进程中,对于一个内核对象进行标识的handle 是完全一样的。

这个时候同一个内核对象就会因为父子加2

所以关闭的时候,就要父子都要调用CloseHandle

这里和之前说的有点不一样。父进程关掉内核对象handle,子进程还是可以操纵这个对象

注:对象handle的继承只会在子进程的时候发生;如果事后父进程又创建了可继承的handle,也不会到子进程

注:子进程并不知道自己继承了啥handle

控制哪个子进程可以继承:

使用 SetHandleInfomation函数

Bool SethandleInfomation( handle hObject, dword dwMask, DWORD dwFlags)

第二种方法:

为对象(共享对象)命名

使用 PCTSTR pszName成员变量

大部分内核对象都可以,有一部分不可以

第三种方法:终端服务命名空间Terminal Service:

一个是全局命名空间

还有就是客户端自己的命名空间client session

作用:多个会话正在运行同一个应用程序,或多个远程桌面使用同一个,这样的安排可以避免彼此之间的干扰

注:当没有任何用户登录的时候

服务会在第一个会话,Session 0中启动

这个会话不会交互

第三种方法:复制对象handle

Bool DuplicateHandle( handle hSourceProcessHandle, handle hSOurceHandle, handle hTargetProcessHandle, phandle phTargetHandle, dword dwDesiredAccess, bool bInheritHandle, dword dwOptions)

流程就是获取一个进程的一个记录值,然后在另一个进程中的Handle Table中创建

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Python中执行系统命令的四种方法(python执行命令行命令)
下一篇:《惢客创业日记》2019.12.11(周三)《与神对话》的启发
相关文章

 发表评论

暂时没有评论,来抢沙发吧~