问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
Windows进程
渗透测试
进程这个观念我们现在都已经很熟悉了,进程是一个程序的运行实例,进程我们可以看做是操作系统为应用程序提供的资源容器,比如内存空间,文件句柄,设备以及网络连接等等。
进程这个观念我们现在都已经很熟悉了,进程是一个程序的运行实例,进程我们可以看做是操作系统为应用程序提供的资源容器,比如内存空间,文件句柄,设备以及网络连接等等。 进程负责管理和组织系统资源,并为应用程序提供所需的上下文以及环境。需要注意的是进程并不直接执行代码,进程只是一个管理结构,他是资源管理的主体,而不是执行代码的主体。 从技术角度来看,线程才是执行代码的实际载体,线程负责在CPU上调度和执行程序指令。所以这里我们就很清楚了。 1. **进程是负责管理和容纳资源 作为程序的执行环境。** 2. **线程是实际执行代码的实体 是进程内的活动单元。** 那么我们在想进程中都有哪些东西呢? 1. **进程中包含了可执行程序**: 可执行程序包括程序的代码和数据,可执行程序包含的内容有代码段,数据段等等。 2. **私有的虚拟地址空间**: 每一个进程都有自己的私有虚拟地址空间,这个空间是由操作系统来提供的,操作系统为每一个进程提供了一个独立的虚拟地址空间,从而保证不同的进程的内存不会相互干扰。也就是说A进程无法去访问B进程的内存,那么同样A进程崩也不会有影响到B进程,其实就是一个隔离性。 3. **主令牌:** 这个其实在令牌窃取那一章节讲过,每一个进程在被创建的时候都会有一块默认令牌,也就是主令牌,这个令牌保存了进程相关的安全信息,比如用户的身份,权限以及安全组相关的信息,这些信息被用来控制进程和线程对系统资源的访问权限。需要注意的是所有线程都共享一个的主令牌,除非我们进行令牌窃取。因为线程是执行代码的主体,如果我们将其令牌窃取为管理员的话,那么我们就可以以管理员的权限来去执行代码。 4. **私有句柄表:** 每一个进程都有一个私有的句柄表,这个表中存储着当前进程正在使用的系统资源,通过这些句柄,进程可以间接访问和操作这些对象,句柄是操作系统在内核和用户程序之间建立的一种抽象的机制,确保程序不需要直接操作系统资源,而是通过句柄来请求操作系统提供服务。 5. **一个或多个线程:** 每一个进程在创建的时候默认有一个主线程,如果一个进程在创建后没有线程,这个进程是无法运行的,因为没有任何线程去执行代码,这个主线程会去执行经典的main/WinMain函数,没有任何线程的进程是没有用的,并且正常情况下会被内核销毁。 如下图包含了进程中的元素。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-8ce80ad7a461e5d43d366c979f59e9b674038797.png) 进程是由一个唯一的进程标识符来进行表示的,这里的进程标识符其实就是PID,只要内核仍然有这个进程对象存在,这个标识符就是唯一的。 虽然多个进程可以基于同一个可执行文件运行,比如Notepad.exe,但是他们在操作系统中的进程实例是独立的,每个进程都有自己的资源,互不干扰,关键点在于每个进程都拥有独立的属性。 比如用户可以启动多个notepad.exe进程,每次启动都会生成一个独立的进程,这些进程共享同一个可执行文件的代码和数据,但是他们在操作系统中是完全独立的实例。 每一个notepad进程都有独立的虚拟地址空间,句柄表,线程等等。需要注意的是PID是绝对不会重复的。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-ef9e4e22a0e0f400b1d205e74324aa5eac21bd58.png) #### 虚拟内存 每一个进程都有自己的虚拟地址空间,在进程创建的初始阶段,其虚拟地址基本上是空的,只有少量的内存区域会被映射。 一般可执行映像和系统所需的基本库会首先被加载到进程的虚拟地址空间中,这里的可执行映像比如是notepad.exe,而基本库是ntdll.dll。随着进程的运行更多的系统库和子系统DLL(比如kernel32.dll user32.dll)会被映射到进程的地址空间中。 一旦第一个线程开始执行,很可能就会开始分配内存了,同时会有更多的DLL装入到地址空间中,这个空间是私有的,所以其他进程也是无法直接访问的。 **每一个进程的虚拟地址空间通常是从0开始的,这意味着在逻辑上,进程可以访问从0开始的地址,但是第一个64KB的地址(从0x0000到0xFFFF)通常被保留,不能分配或使用。** **这是因为在某些操作系统上,这个地址范围可能用于内核数据结构或引导程序。在32位操作系统中,许多系统服务和底层操作需要通过这些地址进行。** 进程的最大地址空间取决于进程的位数(32位或64位)以及操作系统的位数。 1. **32位windows操作系统上的32位进程的默认虚拟地址空间大小为2GB。** 2. **如果32位进程的可执行文件在PE文件头中设置了LARGEADDRESSAWARE标记,地址空间可以扩展到3GB,要实现这一点,映像文件必须在链接时使用此标记。如果未设置该标志,进程仍将受到2GB的限制。** 3. **在64位操作系统上,64位进程的虚拟地址空间大小为8TB(对于windows8和更早的版本)。在Windows8.1及之后的版本中,64位进程的虚拟地址空间可以扩展到128TB。** 4. **在64位操作系统上的32位应用程序进程的默认地址空间仍然是2GB,但是如果链接时设置了largeaddressaware标记,那么其最大的地址空间可以扩展到4GB。** #### 虚拟地址 我们前面说到过,每一个进程都有自己的虚拟地址空间,这就导致了进程内的任何地址都是相对地址,而不是绝对地址。 就比如说0x10000这个地址,它本身并不能告诉我们该地址中存储的具体内容,因为这个地址相对于该进程的虚拟地址空间的。 就比如说0x10000这个地址在A进程中,可能是指向了某个变量或数据结构,而在B进程中0x10000这个地址可能指向完全不同的内容。 **虚拟地址指的是进程的虚拟地址空间中定义的地址,它与物理内存中的实际存储位置之间存在间接的关系。当进程运行时,它使用的都是虚拟地址,而不是直接操作物理地址。** 虚拟地址和物理内存之间的存在间接关系,他们是通过页表实现映射的。 当CPU访问一个虚拟地址时,如果该地址不在RAM中,会产生页错误,操作系统会通过处理该异常将所需数据加载到RAM中并更新页表,然后重试访问。这种机制使得内存管理更加灵活、高效,并简化了程序设计,程序员可以专注于逻辑而非具体的内存管理细节。 如下图: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-f114f3b91c7f88e026c8226ce017ca91b5a25a6c.png) #### 线程 之前我们说到过,线程是真正执行代码的主体,线程是位于进程内的,使用进程提供的资源来完成相关的任务,那么也就是说线程可以共享进程内的相关资源,比如文件句柄,信号量,虚拟内存等等。y线程包含如下重要的信息: 1. **当前访问的模式:** 当前访问的模式分别是用户模式和内核模式。 2. **执行上下文:** 每一个线程维护一组寄存器,包括程序计数器,栈指针以及其他通用寄存器,这些寄存器保存了线程的执行状态。 3. **栈:** 每一个线程都有一个调用栈,用于存储函数调用的局部变量,返回地址以及参数,栈的使用确保了函数调用和返回的正确性,在某些情况下线程可以拥有两个栈,用于不同的任务或实现特殊的调用约定。 4. **线程局部存储(TLS):** TLS允许线程存储私有数据,确保每个线程都有自己的数据副本,而不会受到其他线程的干扰。 5. **基本优先级和当前动态优先级:** 6. **基本优先级**:是由操作系统为线程分配的优先级,用于调度线程的执行顺序。基本优先级在创建线程时设定,并可能根据线程的功能和重要性进行调整。 **动态优先级**:操作系统可以根据线程的运行情况(如CPU使用率、I/O等待时间等)动态调整线程的优先级。这种调整可以帮助操作系统优化系统性能,确保高优先级线程获得更好的响应。 7. **亲和性**:指的是线程被绑定到特定的处理器或处理器组上执行。通过设置亲和性,操作系统可以优化线程的调度,使其在同一个处理器上执行,以利用缓存和减少上下文切换的开销。 亲和性可以提高性能,尤其在多核处理器环境中,因为它减少了缓存失效的可能性,并提高了数据局部性。 #### 线程栈 每一个线程在执行时都有一个栈,用来存储局部变量,传递函数参数,以及在调用函数之前存放其返回地址。线程至少有一个栈位于系统内核空间中,这个栈是比较小的,在32位系统下默认为12KB,在64位操作系统下默认为24KB。 除了内核栈之外,用户模式线程的进程的用户空间地址范围内还可以有一个更大的栈。通常用于存储用户模式下的函数调用信息和局部变量。这个栈的默认大小通常可以扩大到1 MB。用户模式栈的增大允许线程处理更复杂的函数调用和存储更多的局部变量,适合于需要大量堆栈空间的应用程序。 **每个线程的栈是独立的,这意味着线程之间的局部变量和调用信息不会相互干扰。例如,线程1和线程2即使在同一个进程(进程A)中,它们各自的栈是独立的。** #### 系统服务 我们之前再去了解Windows架构的时候,我们都知道有用户模式和内核模式,比如说用户需要去创建一个文件或者分配一块内存,这些操作最终都是由位于内核模式的代码来进行的。 **就比如说有一个记事本,记事本的代码调用Windows API函数CreateFile函数来进行相应,CreateFile函数是在kernel32.dll中导出的,这个API函数是在用户模式下的,所以它本身并不具备访问文件系统的能力,这是因为用户模式下的代码没有足够的权限来直接与硬件或操作系统内核来交互。CreateFile函数在做了基本的检查之后,他会调用更加底层的API函数,也就是NtCreateFile函数,这个函数是在Ntdll.dll中导出的,NtCreateFile函数本质上仍然是在用户模式下执行的,但是他的任务是准备好一切参数和数据,然后发起系统调用,也就是我们常说的syscall。来请求内核模式执行实际的文件操作。** **一旦所有参数准备完毕,NtCreateFile函数会执行一个特殊的CPU指令来触发从用户模式到内核模式的切换。在x64架构中是syscall指令,而在x86架构中是sysenter指令,触发用户模式到内核模式的切换之后,并跳转到内核中的一个固定的位置,这个位置称之为系统服务分发器。** **系统服务分发器是内核中的一个例程,负责根据传入的系统调用号将控制权分配给正确的内核函数。对于`NtCreateFile()`,它的系统服务号指示内核将调用相应的内核函数来处理文件创建或打开操作。** **在内核模式下,操作系统有权访问硬件资源,因此可以直接与文件系统交互,检查文件是否存在,分配相应的资源等。** 如下图: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-6840ece8d5625812dcbfde5ceecc8d03cdf6a6d6.png) #### 系统总体架构 如下图是系统的总体架构: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-6e857380f12dd1d086cbf77a56e0cb2f7ed86bd7.png) ##### 用户进程 用户进程指的是基于映射文件的普通进程,比如notepad.exe cmd.exe explorer.exe等等。 ##### 子系统DLL 子系统DLL指的是实现了子系统的API的动态链接库,比如我们的kernel32.dll user32.dll gdapi.dll等等。 ##### Ntdll.dll Ntdll.dll是一个系统范围的DLL,它实现了Windows原生的API函数,这是用户模式代码的底层,他的作用在于为系统调用提供到内核模式的转换,Ntdll.dll也实现了堆管理,映射加载以及部分用户模式线程池的功能。 ##### 服务进程 服务进程是普通的windows进程,它是与服务控制管理器进行通信的,并允许对他们的生命周期进行一些控制,SCM能够启动,停止,暂停服务,一般服务进程会由某个特殊的Windows账户运行,比如(系统服务)Local System,(网络服务)net work (本地服务)Local Service。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-30744ab6d6cca8a205b18632c07e2a7b3e174835.png) ##### 执行体 执行体位于Nt0skrnl.exe(内核)的高层,它包含了大多数内核代码,其中大部分是各种管理器,比如对象管理器,内存管理器,I/O管理器等等。 ##### 设备驱动程序 设备驱动程序是可以装载的内核模块,代码都在内核中运行,所以具有完全的内核能力。 ##### Win32k.sys Win32k.sys文件是Windows子系统的内核模式组件,本质上他是一个内核模块,用于处理Windows的用户界面部分和经典的图形设备接口API,这意味着所有的窗口操作,例如CreateWindowsEx,GetMessage,PostMessage函数等等。都是由这个组件进行处理的。 ##### 系统进程u系统进程通常用来描述哪些干自己活的进程,终止掉一些进程会导致系统的崩溃,系统的进程比如Smss.exe,Lsass.exe,winlogon.exe,Service.exe 这些进程都是系统进程。 ##### 子系统进程 Windows子系统进程运行的映像文件是Csrss.exe,它可以被视为一个助手进程,帮助内核对Windows系统中运行的进程进行管理。这是一个关键进程,一旦被杀掉,系统就会崩溃。通常每一个会话都有一个Csrss.exe的实例,在运行,因此在标准系统上会存在两个实例—一个对应会话0,另一个对应已登录的用户会话(通常是1)。虽然Csrss.exe是Windows子系统(当前仅有的一个子系统)的“管理器”,但它的重要性远远超出这个角色。 #### 句柄和对象 ##### windows内核对象的类型 在Windows中,内核是通过对象的形式来管理各种的资源的,几乎所有的系统资源都表示为对象,比如文件对象,设备对象,事件对象等等。 不同类型的对象封装了特定的功能和资源。 1. 文件对象: 表示系统中的一个文件。 2. 进程和线程对象: 分别表示系统中的进程和线程。 3. 同步对象: 如事件和信号量,用于线程之间的同步。 这些对象都是由对象管理器管理,他是Windows内核的一个重要组件。 ##### 对象实例和系统空间 n对象的实例实际上是内核空间中的数据结构,这些对象并不直接存在于用户模式的地址空间中,而是存在于 **内核空间**(也称为 **系统空间**)。内核空间受保护,只有内核模式的代码(如驱动程序、内核自身)才能直接访问。用户模式的进程只能通过特定的 API 接口(由 Windows 提供)来访问这些对象,并且这种访问通常是间接的。 **当一个用户模式的进程请求某种资源的时候,比如我们打开一个文件,创建了一个事件或启动了一个线程,内核会在系统空间中创建相应的对象,并返回一个句柄给用户模式的进程。这个句柄允许用户模式的进程间接的操作内核中的对象。** 这里我们可以这样去理解,首先内核对象是Windows内核用来管理各种系统资源的数据结构,比如说进程,文件这些对象的实例都存储在**内核空间**中。那么当我们去打开一个文件的时候,内核会为这个文件去创建一个**文件对象**,创建的这个对象代表这个文件的所有信息和状态,这个对象是处于**内核空间**中的,而不是用户空间中。 那么如果说有某个进程想要去访问这个文件对象的话,就需要进行内核的交互。虽然用户模式不能直接的去访问内核对象,但是可以通过Windows提供的一些API来与内核进行交互。当用户模式进程请求内核中的资源时,系统会返回一个句柄,这是一个间接的引用,用来指向该资源在内核中的对象。 就比如说一个进程调用了CreateFile函数来创建一个文件,Windows内核会为该文件在内核空间中创建一个文件对象,并返回给进程一个文件句柄,那么进程就可以通过这个文件句柄来对文件进行读写的操作,而内核负责将这些操作映射到对应的文件对象上。 ##### 句柄的作用 由于用户模式进程无法直接去访问内核空间中的对象,所以句柄其实就是用户模式进程和内核空间交互的关键机制。 **需要注意的是句柄不是直接指向内核对象的指针,它实际上是指向了一个进程内部维护的一个句柄表。句柄表 是在每个进程中独立维护的,保存了指向内核对象的引用。通过这个句柄表,进程可以通过句柄间接引用内核对象。** ##### 句柄的工作原理 **当一个进程需要去创建或打开一个对象,它可能会通过CreateFile函数向操作系统发送请求,操作系统的内核接收到请求之后,会在内核中创建文件对象或查找文件对象,然后在进程的句柄表中创建一个指向该对象的条目,操作系统会将这个条目的索引值(也就是句柄)返回给进程。** 其实就是间接来操作的。 ##### 内核代码和驱动程序访问内核对象 在内核模式下,内核代码或驱动程序可以通过两种方式来访问内核对象。 1. 第一种就是使用句柄的方式,这也是用户模式最常用的方式,用户模式的进程通过句柄来间接的访问内核对象。 2. 可以直接使用内核对象的指针来操作对象。但在某些场景下,特别是当用户模式的代码将句柄传递给内核模式时**,内核需要将这个句柄转换为指针才能直接操作对象。** 当内核代码或驱动程序接收到用户模式传递过来的句柄之后,他们不能直接使用这个句柄,而是需要通过`ObReferenceObjectByHandle`这个函数将这个句柄转换为指向对象的直接指针,这样内核代码就可以直接操作这个对象了。 ##### 对象名称 某些类型的内核对象可以有名称,我们可以通过其名称来获取对象的句柄。 进程和线程是特殊的情况,他们没有名称,而是使用标识符ID来标识,每一个进程和线程都有一个唯一的数字ID,进程为PID,而线程为TID。 所以如果需要打开进程或线程只需要去传递进程ID和线程ID即可。 使用名称调用 **`Create`** 函数时,如果对象不存在则会创建新对象;如果对象已存在,则返回一个指向该对象的句柄。通过检查 **`GetLastError`** 的返回值可以确定是否创建了新对象或只是打开了现有对象。这种机制允许多个进程或线程能够共享和同步对同一对象的访问。 如下代码: ```c HANDLE hMutex = CreateMutex(NULL, FALSE, "MyMutex"); if (hMutex == NULL) { // 处理错误 } else if (GetLastError() == ERROR_ALREADY_EXISTS) { // 对象已经存在,hMutex 是指向现有互斥量的句柄 printf("Mutex already exists.\n"); } else { // 成功创建了一个新的互斥量 printf("Mutex created successfully.\n"); } ``` 提供给 **`Create`** 函数的对象名称并不是最终的名称。在实际创建或打开对象时,Windows 操作系统会在名称前添加特定的前缀,以区分不同会话中的对象。在名称前加上 `\Sessions\x\BaseNamedObjects\`,其中 `x` 是调用者的会话标识符(Session ID)。这允许 Windows 区分不同用户会话中创建的对象。例如,如果会话 ID 为 1,则完整的名称可能为 `\Sessions\1\BaseNamedObjects\MyObject`。对于会话 ID 为 0 的情况,名称前缀为 `\BaseNamedObjects\`,这表示这是一个特权会话,通常是用于服务或系统进程的会话。 每一个进程有一个私有的,指向内核对象的句柄表格,而无论这些对象有没有名称,这个表格可以通过Process Explorer或Process Hacker来进行查看。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-585bc9421e77557275c0bed4b72b9ca8e237f98e.png) ##### 访问已经存在的对象 比如说Process Explorer的句柄视图中的访问(access)列显示了打开或者创建句柄时使用的访问掩码(access mask)。访问掩码是允许特定的句柄做哪些操作的关键。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-590d219034c8585ea2624b03f1765766ea01dcef.png) 我们可以点击View->Select Columns->Handle。 在Handle这里将其全部勾选上即可。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-b1cad18144ee64ef07b307896dbb9ce3475fe075.png) 那么这个掩码如何去看呢? 掩码主要是用来定义进程对内核对象的访问权限,其中每一位都代表一种特定的权限。 比如0x001F0001这个掩码。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-60360caaa7af9094a316450287d4caee9260845a.png) 0x001F0001转换为二进制就是111110000000000000001。 所对应的权限其实就是 删除对象。 读取对象的控制权限。 修改对象的DACL。 修改对象的拥有者。 对对象进行同步。 例如,假设客户代码希望终止一个进程,它必须指定含有的访问掩码来调用OpenProcess函数得到进程的句柄,否则得到的句柄是无法用来终止进程的。如果调用成功了,用得到的句柄调用TerminateProcess才会成功。这里有一段用户模式代码,用来终止一个给定进程标识符的进程: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-5f2baf8cbbee4b44adf409c8fba70eca2a353c39.png) 我们可以在一个句柄上双击就可以查看到他的属性了,在属性中包含了句柄的名称,类型,描述,以及在内核内存中的地址。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/12/attach-54b16f8afd582aee73dc47007ee1b5893aa4e8c1.png) 大致总结: ```php 关于进程相关的概念: 1.每创建一个进程都有一个独立的私有虚拟地址空间 这也就意味着进程之间其实是隔离的 A进程无法访问B进程的资源 A进程崩溃也不会影响B进程 2.每创建一个进程都有一个主令牌 主令牌中包含了用户的主体信息以及权限等信息 这个令牌决定了进程和线程可以访问哪些系统资源 需要注意的是所有线程都共享进程的主令牌 3.线程是执行代码的主体 那么如果窃取了管理员的令牌 那么线程就可以以管理员的身份去执行代码 4.每一个进程中都有多个线程 线程可以共享进程中的资源 这也意味着如果一个线程崩溃了,那么可能会影响整个进程。 在进程创建的时候默认肯定是有一个主线程的 如果一个进程一个线程都没有 这个进程没有任何意义 5.进程中包含了可执行程序的代码和数据 6.每一个进程都有一个句柄表 这个表中存储着当前进程正在使用的系统资源 通过这个句柄我们可以间接的访问和操作这些对象 句柄其实就是操作系统在内核和用户程序之间建立的一个抽象的机制 关于虚拟内存相关的概念: 1.32位windows操作系统上的32位进程默认的虚拟地址空间的大小为2GB 2.如果32位可执行文件的PE文件头中设置了LARGEADDRESSAWARE标记 地址空间大小可以扩展到3GB 3.在64位操作系统上 64位进程的虚拟地址空间对8TB windows8.1及版本之后 扩展到了128TB 4.在64位操作系统上 32位应用程序的虚拟地址空间大小还是2GB 但是如果设置了largeaddressware标记的话 会扩展到4GB 关于线程的概念: 1.每一个进程中都有多个线程 进程中的线程共享进程中的资源 线程是执行代码的主体 2.每一个线程都维护了一组寄存器 这些寄存器其实保存了线程的执行状态 其实就是线程的上下文 CONTEXT结构 3.每一个线程都有一个调用栈,用于存储函数调用的局部变量,返回地址以及参数 当线程调用函数时,局部变量和其他相关的信息会被推入到栈中 当函数返回时,这些信息会被弹出栈 其实也是为了保证线程保持独立性,不同的线程执行不同的代码 为了保证各个线程之间互不干扰 每一个线程都有一个独立的调用栈 4.每一个线程都有自己的TID 称之为线程ID 用来识别和区分不同的线程 5.关于优先级 这里分为基本优先级和当前动态优先级 基本优先级是由操作系统为线程分配的优先级 用于调度线程的执行顺序 基本优先级在创建线程的时候就设定了。 动态优先级是由操作系统根据线程的运行情况,这里的运行情况指的是CPU的使用率 I/O等待时间等 动态调整线程的优先级 这种调整可以帮助系统优化其性能 6.线程的生命周期,从创建,就绪,运行,等待/阻塞,终止结束。 7.线程可以分为内核线程和用户线程 内核线程: 内核线程由操作系统内核直接管理 操作系统负责调度和管理内核线程的声明周期 用户线程: 由用户空间的线程库管理,操作系统对其不可见,用户线程的调度在用户空间完成,效率是比较高的 关于系统服务的概念: 当调用CreateFile函数时,会调用更加底层的NtCreateFile函数 NtCreateFile函数是在Ntdll.dll中导出的,但是NtCreateFile函数仍然是在用户模式下的,NtCreateFile本质上的 任务其实就是准备好参数和数据,然后进行系统调用,统称为syscall。 准备好一些参数和数据之后,NtCreateFile函数执行syscall指令来触发从用户模式到内核模式的切换,在64为操作系统上是syscall,在x86操作系统上是sysenter指令。 切换之后会跳转到系统服务分发器这里 系统服务分发器会根据寄存器中的系统调用号来找到对应的内核服务例程,这里一般都是eax寄存器。 最后会调用内核模式中NtCreateFile函数来处理文件的创建和打开,通常位于ntoskrnl.exe中。 关于句柄和对象的概念: 1.内核是通过对象的形式来管理各种资源的,操作系统上的文件基本上我们都可以称之为对象,比如文件对象,设备对象,事件对象等等。 2.对象的实例实际上是内核空间中的数据结构,这些对象并不直接存在于用户模式地址空间中,而是存在于内核空间中。 3.当一个用户模式的进程请求各种资源的时候,比如我们打开了一个文件,创建了一个事件或启动了一个线程,内核会在系统空间中创建相应的对象,并返回一个句柄给用户模式的进程。 这个句柄允许用户模式的进程间接的操作内核中的对象。 4.一个进程调用了CreateFile函数来创建一个文件,Windows内核会为该文件在内核空间中创建一个文件对象,并返回给进程一个文件句柄, 那么进程就可以通过这个文件句柄来对文件进行读写的操作,而内核负责将这些操作映射到对应的文件对象上。 5.用户模式进程无法直接访问内核空间中的对象,所以句柄其实就是用模式进程和内核空间交互的关键机制。 句柄不是直接指向内核对象的指针,它实际上是指向了一个进程内部维护的一个句柄表,句柄表是每一个进程独立维护的 保存了指向内核对象的引用, 通过这个句柄表进程可以通过句柄间接引用内核对象。 句柄的工作原理如下: 当一个进程需要去创建或打开一个对象时,它可能会通过CreateFile函数向操作系统发送请求,操作系统的内核接收到请求之后, 会在内核中创建文件对象或查找文件对象,然后在进程的句柄表中创建一个指向该对象的条目,操作系统会将这个条目的索引值(也就是句柄)返回给进程。 内核代码和驱动程序访问内核对象: 1.使用句柄的方式,用户模式进程通过句柄间接的访问内核对象。 2.使用内核对象的指针来操作对象,当用户模式的代码将句柄传递给内核模式时,内核需要将这个句柄转换为指针才能去操作这个对象。 当内核代码或驱动程序接收到用户模式传递过来的句柄之后,需要通过ObReferenceObjectByHandle函数来将其转换为指向对象的指针才可以去操作这个对象。 ``` 如上笔记参考: windows内核编程
发表于 2025-01-06 09:00:02
阅读 ( 586 )
分类:
二进制
2 推荐
收藏
0 条评论
请先
登录
后评论
南陈
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!