文章目录

  • 线程切换与TSS
    • 内核堆栈
    • 内核堆栈结构
    • 调用API进零环
    • SwapContext代码分析
  • 线程切换与FS
    • SwapContext代码分析
  • SwapContext的其他问题
    • SwapContext有几个参数 怎么判断出来的?
    • SwapContext在哪里实现了线程切换?
    • 0环的ExceptionList是在哪里备份的

线程切换与TSS

SwapContext这个函数是Windows线程切换的核心,无论是主动切换还是系统时钟导致的线程切换,最终都会调用这个函数

在这个函数除了切换堆栈以外,还做了很多其他事情,下面就来学习一下线程切换与TSS的关系

内核堆栈

每一个线程都有一个内核堆栈,当API调用进零环的时候,必然要切换堆栈这个堆栈就是当前线程的零环堆栈。那这个线程零环的堆栈去哪里找呢?

KTHREAD结构体中有三个成员:InitialStack是当前堆栈的栈底,KernelStack是当前堆栈的栈顶,StackLimit是堆栈的边界。也就是说如果我们找到了这三个成员也就找到了内核堆栈。

内核堆栈结构

内核堆栈从结构上大体分成两部分,第一部分从InitialStack开始的0x210个字节存储的是当前线程用到的浮点寄存器的值。

从0x210再往后就是Trap_Frame结构体。完整结构如图:

调用API进零环

普通调用:通过TSS.ESP0得到零环堆栈

快速调用:从MSR得到一个临时的0环栈,代码执行后仍然通过TSS.ESP0得到当前线程的0环栈

我们找到KiFastCallEntry的代码,0FFDFF000的位置是KPCR,首先找到KPCR偏移为0x40的位置TSS,然后再找到TSS偏移4的位置ESP0,把这个值赋给了当前的esp。然后才开始往堆栈里压入值。

那么问题来了,**TSS中的ESP0来自于哪?**答案在SwapContext的代码里。

SwapContext代码分析

Intel设计TSS的目的是为了任务切换(线程切换),但Windows与Linux并没有使用,而是采用堆栈来保存线程的各种寄存器。

那么这里就有一个问题,**一个CPU只有一个TSS,但是线程很多,如何用一个TSS来保存所有线程的ESP0呢?**答案都在SwapContext的代码里

找到SwapContext中与TSS相关的代码,ebx就是当前CPU对应的结构体KPCR,通过KPCR找到TSS存到ecx里,

TSS偏移4的位置是ESP0,接着将eax存储到ESP0,继续往上找一下eax的值。

首先将目标线程的栈底存储到eax

此时eax指向上图的的InitStack

接着减去0x210

此时eax指向Trap_Frame结构

接着再减去0x10,也就是4个成员

Trap_Frame最底下的四个成员是给虚拟8086模式用的。通过刚才的计算得出,当前的eax指向的是0x078的位置SS

.text:00469B1C                 mov     ecx, [ebx+40h]  ; 通过KPCR取出TSS
.text:00469B1F                 mov     [ecx+4], eax    ; 将Trap_Frame.ESP0存到TSS.ESP0

这就是SwapContext函数对TSS的使用,它会将Trap_Frame.ESP0存到TSS.ESP0。

到这里,解决了之前提出的两个问题

**TSS中的ESP0来自于哪?**来自于0环的Tram_Frame结构体

**一个CPU只有一个TSS,但是线程很多,如何用一个TSS来保存所有线程的ESP0呢?**在发生线程切换的时候,SwapContext会将目标线程的ESP0存到TSS中,然后开始切换线程。就是说TSS永远存储的是当前线程的ESP0

TSS中除了ESP0之外还用到了一个值就是CR3,SwapContext会将当前TSS中的CR3修改为目标进程的CR3,然后切换CR3。

下面一行代码将当前线程的IO权限位图存到了TSS里。这个成员在Windows2000以后不用了

**结论:**Intel提供的TSS在Windows里只有三个成员是有意义的:ESP0 CR3和IO权限位图

线程切换与FS

FS:[0]寄存器在3环的时候指向TEB,进入0环后FS:[0]指向KPCR。

系统中同时存在很多个线程,这就意味着FS:[0]在3环的时候指向的TEB要有多个,有一个线程就要有一个TEB,但是在实际使用中我们发现在3环查看不同线程的FS寄存器时,FS的段选择子都是相同的,那么是如何实现通过一个FS寄存器指向多个TEB呢?

答案依然在SwapContext函数的代码里。

SwapContext代码分析

找到SwapContext与TEB相关的代码

首先取出目标线程的TEB 放到eax里

.text:00469B67                 mov     ecx, [ebx+3Ch]  ; 通过KPCR找到GDT表

接着通过KPCR找到GDT表,存到ecx

.text:00469B6A                 mov     [ecx+3Ah], ax

这里的ax是TEB的低16位,

而ecx+3A就是段描述符低4个字节的16-31位Base Address,也就是将TEB的低16位写到段描述符的16-31位。

.text:00469B6E                 shr     eax, 10h

将eax右移16位,这样就能得到TEB地址的高16位。因为低16位已经写到段描述符里了。

.text:00469B71                 mov     [ecx+3Ch], al   ; 将低8位写到段描述符0-7的位置
.text:00469B74                 mov     [ecx+3Fh], ah   ; 将高8位写到段描述符31-24的位置

高16位又分成两部分写到段描述符中,先将低8位写到段描述符0-7的位置,再将高8位写到段描述符31-24的位置。

通过这几行代码就将新的线程的TEB的地址写到了当前GDT表的段描述符的基址中。

这就回答了刚才的问题:如何实现通过一个FS寄存器指向多个TEB?

因为FS段选择子虽然没有发生变化,但是在线程切换的时候,会修改段选择子所指向的段描述符的基址为新的线程的TEB的地址。

SwapContext的其他问题

SwapContext有几个参数 怎么判断出来的?

四个参数,但真正有用的只有三个,分别是:

  • esi:当前线程结构体ETHREAD指针
  • edi:要切换的线程结构体ETHREAD指针
  • ebx:KPCR

首先找到SwapContext的父函数KiSwapContext

0FFDFF01Ch存储的就是KPCR,所以参数ebx就是KPCR

.text:004699CE                 mov     edi, [ebx+124h] ; KPCR+0x124是当前线程的KTHREAD结构体

ebx是KPCR,KPCR+0x124的位置就是当前线程的KTHREAD结构体

.text:004699CC                 mov     esi, ecx        ; ecx是上一层调用的函数传进来的 是要切换线程的KTHREAD

而esi来自于ecx,ecx是上一层调用的函数传进来的 是要切换线程的KTHREAD

找到上一层的函数,ecx来自于eax,是KiFindReadyThread的返回值,这个函数会查找一个就绪线程返回KTHREAD结构体

SwapContext在哪里实现了线程切换?

线程切换的本质就是切换堆栈

这行代码将目标线程的KernelStack存到ESP里,这行代码以后另一个线程复活

0环的ExceptionList是在哪里备份的

这行代码会将ExceptionList存储到ecx,然后将ExceptionList保存到堆栈

进程线程005 SwapContext函数分析相关推荐

  1. 【操作系统/OS笔记10】进程/线程的调度原则、调度算法、实时调度、多处理器调度、优先级反转

    本次笔记内容: 8.1 背景 8.2 调度原则 8.3 调度算法1 8.4 调度算法2 8.5 实时调度 8.6 多处理调度与优先级反转 文章目录 CPU调度背景 上下文切换 CPU调度 在进程/线程 ...

  2. Python之路 34:并发与并行、锁(GIL、同步锁、死锁与递归锁)、信号量、线程队列、生消模型、进程(基础使用、进程通信、进程池、回调函数)、协程

    内容: 同步锁 死锁.递归锁 信号量和同步对象(暂时了解即可) 队列------生产者和消费者模型 进程(基础使用.进程通信.进程池.回调函数) 协程 一.并发并行与同步异步的概念 1.1.并发和并行 ...

  3. 浏览器层面优化前端性能(1):Chrom组件与进程/线程模型分析

    现阶段的浏览器运行在一个单用户,多合作,多任务的操作系统中.一个糟糕的网页同样可以让一个现代的浏览器崩溃.其原因可能是一个插件出现bug,最终的结果是整个浏览器以及其他正在运行的标签被销毁. 现代操作 ...

  4. 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 注入工具的 main 函数分析 )

    文章目录 一.注入流程 二.注入工具的 main 函数分析 一.注入流程 开始分析 [Android 逆向]Android 进程注入工具开发 ( 编译注入工具 | 编译结果文件说明 | 注入过程说明 ...

  5. windows内核情景分析---进程线程2

    二.线程调度与切换 众所周知:Windows系统是一个分时抢占式系统,分时指每个线程分配时间片,抢占指时间片到期前,中途可以被其他更高优先级的线程强制抢占. 背景知识:每个cpu都有一个TSS,叫'任 ...

  6. windows内核情景分析---进程线程1

    本篇主要讲述进程的启动过程.线程的调度与切换.进程挂靠 一.进程的启动过程: BOOL CreateProcess ( LPCTSTR lpApplicationName,               ...

  7. Linux系统编程----15(线程与进程函数之间的对比,线程属性及其函数,线程属性控制流程,线程使用注意事项,线程库)

    对比 进程 线程 fork pthread_create exit (10) pthread_exit (void *) wait (int *) pthread_join (,void **)阻塞 ...

  8. 0820Python总结-线程队列,进程池和线程池,回调函数,协程

    一.线程队列 from queue import Queue put 存 get 取 put_nowait 存,超出了队列长度,报错 get_nowait 取,没数据时,直接报错 Linux Wind ...

  9. 进程间的数据共享、进程池的回调函数和线程初识、守护线程

    一.进程的数据共享 进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的 虽然进程间数据独立,但可以通过Manager实现数据共享.把所有实现了数据共享的比较便捷的类都重新又封装了 ...

最新文章

  1. C# 虚函数和重载函数
  2. python自动化测试locksetting/gatekeeper/keymaster/vts等
  3. BUUCTF(pwn)mrctf2020_easyoverflow
  4. zw版【转发·台湾nvp系列Delphi例程】HALCON DispCross
  5. Windows mysql boost_Win7下Boost库的安装
  6. 联想即将推出预装 Fedora 的 ThinkPad 笔记本电脑
  7. 剑指offer:数据流中的中位数(小顶堆+大顶堆)
  8. 英伟达推出新款“煤气灶”Titan RTX,售价近2万,并开源PhysX SDK
  9. 在centos7中安装nodejs(npm )
  10. Deepin安装Eclipse
  11. 非极大值抑制(Non-Maximum Suppression)
  12. RANSAC介绍(Matlab版直线拟合+平面拟合)
  13. Python 办公自动化之全网最强最详细PDF 文件操作手册
  14. 深入理解Instrument
  15. 泰拉瑞亚服务器config修改,《泰拉瑞亚》游戏配置怎么修改 游戏配置修改办法推荐...
  16. 【Django下载文件-Kml文件下载】
  17. 一、注册功能怎么测试
  18. 美光科技股价上涨13% 创下自2011年12月以来最大单日涨幅
  19. 如何查看小方侦测云存储_小方智能摄像机和手机怎样连接?
  20. 全栈之路-前端篇 | 第二讲.基础前置知识【应用服务端与编程语言】学习笔记

热门文章

  1. C++:C++语言入门级基础知识考察点回顾之基本数据类型、流程控制
  2. CSDN:因博主近期注重写专栏文章(已超过150篇),订阅博主专栏人数在突增,近期很有可能提高专栏价格(已订阅的不受影响),提前声明,敬请理解!
  3. DL之RNN:人工智能为你写代码——基于TF利用RNN算法实现生成编程语言代码(C++语言)、训练测试过程全记录
  4. CV之IC: 图像描述(Image Captioning) 的简介、使用方法、案例应用之详细攻略
  5. Hyperopt中文文档:Cite引用
  6. [Swift]LeetCode468. 验证IP地址 | Validate IP Address
  7. js格式化文件大小, 输出成带单位的字符串工具
  8. 使用dtd--属性声明
  9. Nrf51822中设置128bit UUID service
  10. UIView 学习知识点