WIN32_线程(上)

1. 线程基本概念

1.1 线程与进程的关系

  • 线程是操作系统调度的最小单位: 线程被包含在进程内部,操作系统的调度、进程切换等关键操作均以线程为基础单位进行。线程拥有自己的一套寄存器数据以及堆栈信息。

  • 进程可包含多个线程: 一个进程能并发运行多条线程,进程的存在与运行依赖于主线程的启动以及数据的执行 ------ 正是由于拉起主线程并执行相关数据,一个程序才被称为进程。

1.2 线程的特性

  • 具备独立执行环境: 每条线程都拥有专属的寄存器和堆栈信息,形成独立的线程上下文环境,因此各线程的执行过程互不干扰、相互独立。

  • 共享进程资源: 属于同一进程的线程,可共同使用该进程的数据段、代码段等资源,例如能够同时读取相同的二进制代码。

  • 可提高执行效率: 多线程模式下,不同任务可实现并行执行,相较于单线程能有效避免因某个任务阻塞而影响整体运行的情况,从而显著提升执行效率。


2. 线程与进程的关系

2.1 进程的核心特性

  • 独立内存空间: 进程拥有专属的虚拟内存空间,范围从 0 开始,直至基于进程位数所划定的区间,内存空间具有独立性。

  • 独立执行环境: 操作系统会对进程所需的各类资源进行维护与修正,为其提供可独立运行的执行环境。

  • 进程间通信机制: 不同进程间无法直接交互数据,需通过特殊方式实现通信,例如共享内存(如 mmap、VC lock 等具体方式)。

2.2 线程的核心特性

  • 进程的组成部分: 线程并非独立存在,而是运行在进程内部的执行单元。

  • 共享内存空间: 同一进程中的线程共享该进程的内存空间,无需额外机制即可访问进程资源。

  • 独立执行环境: 尽管共享进程内存,但每条线程拥有自己的寄存器、栈信息等专属执行环境,保证了执行的独立性。

  • 线程间通信方式: 线程间可直接读写进程中的数据资源,以此实现同步、互斥等交互需求,通信更为便捷。

2.3 线程与进程的关系

  • 调度与分配角色: 进程是操作系统进行资源分配和调度的基本单位,而线程是 CPU 调度的最小单位。

  • 包含关系: 线程是进程内部的执行单位,一个进程至少包含一个主线程,可在此基础上扩展多个线程。


3. _EPROCESS

3.1 内核与应用层的关系

应用层在获取核心资源方面存在局限性,许多关键的系统管理内容(如进程管理的核心结构等)都存在于内核层面,这使得内核成为掌控系统核心功能的关键区域。

3.2 进程结构体解析

进程包含两个重要结构体:

  • KPROCESS: 属于内核层面的结构体,存储着与进程内核态运行相关的关键信息。

  • EPROCESS: 为执行体结构体,涵盖了进程在执行过程中的众多详细信息,是进程管理的重要数据结构。

3.3 关键函数与机制

  • WaitForSingleObject: 该函数的作用是等待可等待的内核对象,通过它能够实时知晓进程或线程是否结束,在进程和线程的状态监控中发挥重要作用。

  • CR3 寄存器: 它是虚拟地址转换过程中不可或缺的一环。基于操作系统的分页模式,CR3 寄存器帮助解析虚拟地址所对应的物理地址,从而确保每个进程的虚拟内存空间相互独立,互不干扰。

3.4 线程与进程的关联

线程是进程的组成部分,进程结构体中包含线程链表,通过该链表可以指向进程所包含的线程,进而体现出各个线程的状态,清晰展现出进程与线程之间的从属关系。

3.5 进程优先级机制

由于电脑的资源是有限的,进程优先级决定了系统资源的分配顺序。用户可以在相关系统设置中对进程优先级进行调整,以适应不同的应用场景需求。

3.6 进程 ID 的特性

每个进程都拥有唯一的 ID,这一 ID 是进程的标识。用户可以通过多种方式(如任务管理器、系统命令等)查看进程 ID,并且内核中记录的进程 ID 与任务管理器中显示的信息是相互关联、保持一致的。

3.7 进程的遍历与隐藏

  • 遍历进程: 存在多种方法可以对系统中的进程进行遍历,以获取进程的相关信息。

  • 复杂情况: 当遇到有保护机制的进程时,调试工作会变得较为复杂。

3.8 调试相关内容

  • 调试实现: 可以通过 CreateProcess 和 DebugActiveProcess 等 API 来实现对进程的调试。

  • 复杂情况: 当遇到有保护机制的进程时,调试工作会变得较为复杂。

3.9 句柄表的作用

当进程创建或打开内核对象时,会得到一个句柄。句柄表的功能是帮助找到该句柄所对应的真正内核对象,其存在防止了应用层对内核对象进行随意修改,保障了系统的稳定性。

3.10 进程保护方式

通过设置 ProtectProcess 标志位,可以使进程难以被打开或附加,从而实现类似 "拒绝访问" 的保护效果,增强进程的安全性。

3.11 内核与 API 的关系

V3 核心编程的重点在于熟悉 API 的使用,编程时需按照规定格式调用 API,并通过返回值和错误处理机制来修正代码,而这些 API 的核心功能最终由内核来处理实现。


4. ETHREAD

4.1 线程的基本属性与验证

  • 线程结构体中的 Key32 可用于验证线程所属进程,明确体现了线程是进程的一部分这一核心关系。

4.2 相关内核对象与 API

  • Dispatch Handler 属于可等待的内核对象,可通过 WaitForSingleObject 这一 API 进行等待操作。

4.3 线程栈区特点

  • 线程拥有应用层和内核层两个栈区,由于权限差异,二者无法共享。

  • 栈区存在栈底和栈顶,初始化的栈底在 thread 切换时会被保留;start limit 代表栈顶,current start 则是当前栈顶,用于记录线程切换情况。

4.4 线程的锁、运行标记与状态

  • threed lock 可理解为线程锁,用于线程同步等操作。

  • running 作为运行标记,0 表示未运行,1 表示运行,线程存在多种不同状态。

  • 线程结构体多描述线程的就绪、调度、等待等状态,且通过 three list entry 与相关项形成双向循环链表,关联活跃线程,可从中获取线程 ID、创建与结束时间等信息。

4.5 线程类型与优先级

  • 线程分为普通线程和内核线程,PS System32 可理解为内核线程。

  • 线程优先级从 0 到 31 共 32 个等级,数字越大权限越高;CPU 每个核有 32 个调度链表,按优先级从大到小进行调度。

4.6 线程的创建与归属

  • 线程由进程创建,存在 "亲爹" 和 "干爹" 的情况,即线程创建后可以隶属于其他进程。

4.7 API 调用与内核的关系

  • 应用层执行 API 最终会通过 SSDT 表找到内核函数来完成调用,过程中会传递符号以寻找对应的内核函数。

  • 熟悉 API 参数有助于深入进行内核开发,而 API 调用的成功与否与内核知识、权限等因素相关。


5. 线程创建执行

5.1 线程控制

  • 创建 :

    • 线程是通过CreateThread创建的,该API会为线程分配必要的资源且返回内核对象句柄。
    • 创建线程可以通参数指定线程属性,线程运行状态(创建运行线程,创建挂起进程)。
  • 执行 :

    • 使用CreateThread创建线程的时候,当参数不指定Create_SusPended,线程会进去就绪等待调度。
    • 指定Create_SusPended时,线程会进入挂起状态,会使用ResumeThread恢复线程运行状态。
  • 挂起 : SuspendThread

  • 恢复 : ResumeThread

  • 终止: TerminateThread

  • 远程: CreateRemoteThread

5.2 线程创建

系统规定的固定格式:

c 复制代码
返回值  系统调用约定  		函数名 (参数)
DWORD   WINAPI(_stdcall)    WorkThread(LPVOID lp)

5.3 并行输出

5.3 主线程阻塞,子线程继续执行

主线程的彻底结束,才能停止子线程的继续执行。


6. 捕获线程状态

通过给子线程设置条件,使得结束状态:

6.1 设置条件等待

WaitForSingleObject的调用可以使得子线程的彻底结束才能往下继续执行主线程代码。

当然我们可以根据捕获该API的返回值判断事件。

6.2 主线程等待多子线程结束再执行


7. 等待就绪线程

通过挂起和恢复的这个流程我们可以看到,当子线程被挂起的时候并不影响主线程往下执行,但是当子线程恢复后,主线程不能立刻退出进程,否则子线程还没来得及执行就被杀死了。


8. 控制挂起恢复

通过这个例子我们可以清晰观察到线程经过创建-挂起-恢复-结束 的一系列流程,以及数据的输出-中断-输出,能够清晰的看到这个生命周期。


9. 销毁终止释放

9.1 TerminateThread终止指定线程

在这个练习中主要依靠TerminateThread结束指定进程,然后GetExitCodeThread检索终止状态达到一个结束并获取状态的一个方法。

9.2 完成条件关闭线程

在这个练习中,我们在子线程完成条件以后通过ExitThread来结束线程,再由 WaitFortSingleObject接受状态值完成放行,再由GetExitCodeThread接受线程终止状态,最后关闭句柄,完成一系列流程。


10. 远程线程调用

打开进程获取句柄,然后根据句柄以及另一个进程中的函数地址作为参数放入GreateRemoteThreadAPI中完成在另一个进程中创建线程。


11. 线程上下文环境

程序创建一条循环常驻的子线程,主线程等待三秒后挂起子线程,通过 CONTEXT 读取其全部寄存器上下文,读取完毕再恢复线程运行,演示调试器赖以实现的线程暂停与寄存器获取底层逻辑。


12. 简易进程线程信息

这段代码通过 Windows 系统快照 API,先遍历系统中所有进程并打印进程 ID,每获取一个进程后,再拍摄全系统线程快照,并通过进程 ID 匹配筛选出属于当前进程的所有线程,最终打印出每个进程对应的线程 ID,实现枚举系统全部进程及所属线程的功能。


本章完~

相关推荐
AI科技星16 小时前
数术工坊 · 第四卷 橡皮泥江湖(拓扑学)【完整定稿】
c语言·开发语言·汇编·electron·概率论·拓扑学
iCxhust1 天前
C# 生成命令行程序 将hex格式烧录程序转换成bin烧录格式
开发语言·汇编·单片机·嵌入式硬件·c#·微机原理
iCxhust2 天前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
hhcgchpspk2 天前
汇编语言传递数据和地址的误区
汇编·笔记·nasm·masm
iCxhust2 天前
MTK8088单板机制作(一)时钟电路
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust2 天前
8086 汇编位测试使用方法
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust2 天前
用汇编在8088单板机上创建一个进程
汇编·微机原理
AI科技星3 天前
第三卷:质数王朝志(全卷定稿)
c语言·开发语言·汇编·electron·概率论
H Journey3 天前
汇编基础知识:CPU的寻址逻辑
汇编·cpu寻址