精华内容
下载资源
问答
  • TSS任务状态段

    2019-07-30 14:51:00
    任务状态段寄存器TR的可见部分含有当前任务的任务状态段描述符的选择子,TR的不可见的高速缓冲寄存器部分含有当前任务状态段的段基地址和段界限等信息。  TSS在任务切换过程中起着重要作用,通过它实现任务的挂起...

    任务状态段(Task State Segment)是保存一个任务重要信息的特殊段。任务状态段描述符用于描述这样的系统段。任务状态段寄存器TR的可见部分含有当前任务的任务状态段描述符的选择子,TR的不可见的高速缓冲寄存器部分含有当前任务状态段的段基地址和段界限等信息。

      TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到TR所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换。
      任务状态段TSS的基本格式如下图所示。

     























    BIT31—BIT16 BIT15—BIT1 BIT0 Offset
    0000000000000000 链接字段 0
    ESP0 4
    0000000000000000 SS0 8
    ESP1 0CH
    0000000000000000 SS1 10H
    ESP2 14H
    0000000000000000 SS2 18H
    CR3 1CH
    EIP 20H
    EFLAGS 24H
    EAX 28H
    ECX 2CH
    EDX 30H
    EBX 34H
    ESP 38H
    EBP 3CH
    ESI 40H
    EDI 44H
    0000000000000000 ES 48H
    0000000000000000 CS 4CH
    0000000000000000 SS 50H
    0000000000000000 DS 54H
    0000000000000000 FS 58H
    0000000000000000 GS 5CH
    0000000000000000 LDTR 60H
    I/O许可位图偏移 000000000000000 T 64H

     

      从图中可见,TSS的基本格式由104字节组成。这104字节的基本格式是不可改变的,但在此之外系统软件还可定义若干附加信息。基本的104字节可分为链接字段区域、内层堆栈指针区域、地址映射寄存器区域、寄存器保存区域和其它字段等五个区域。

    1.寄存器保存区域

      寄存器保存区域位于TSS内偏移20H至5FH处,用于保存通用寄存器、段寄存器、指令指针和标志寄存器。当TSS对应的任务正在执行时,保存区域是未定义的;在当前任务被切换出时,这些寄存器的当前值就保存在该区域。当下次切换回原任务时,再从保存区域恢复出这些寄存器的值,从而,使处理器恢复成该任务换出前的状态,最终使任务能够恢复执行。
      从上图可见,各通用寄存器对应一个32位的双字,指令指针和标志寄存器各对应一个32位的双字;各段寄存器也对应一个32位的双字,段寄存器中的选择子只有16位,安排再双字的低16位,高16位未用,一般应填为0。

    2.内层堆栈指针区域

      为了有效地实现保护,同一个任务在不同的特权级下使用不同的堆栈。例如,当从外层特权级3变换到内层特权级0时,任务使用的堆栈也同时从3级变换到0级堆栈;当从内层特权级0变换到外层特权级3时,任务使用的堆栈也同时从0级堆栈变换到3级堆栈。所以,一个任务可能具有四个堆栈,对应四个特权级。四个堆栈需要四个堆栈指针。
      TSS的内层堆栈指针区域中有三个堆栈指针,它们都是48位的全指针(16位的选择子和32位的偏移),分别指向0级、1级和2级堆栈的栈顶,依次存放在TSS中偏移为4、12及20开始的位置。当发生向内层转移时,把适当的堆栈指针装入SS及ESP寄存器以变换到内层堆栈,外层堆栈的指针保存在内层堆栈中。没有指向3级堆栈的指针,因为3级是最外层,所以任何一个向内层的转移都不可能转移到3级。
      但是,当特权级由内层向外层变换时,并不把内层堆栈的指针保存到TSS的内层堆栈指针区域。实际上,处理器从不向该区域进行写入,除非程序设计者认为改变该区域的值。这表明向内层转移时,总是把内层堆栈认为是一个空栈。因此,不允许发生同级内层转移的递归,一旦发生向某级内层的转移,那么返回到外层的正常途径是相匹配的向外层返回。
     
    3.地址映射寄存器区域
      从虚拟地址空间到线性地址空间的映射由GDT和LDT确定,与特定任务相关的部分由LDT确定,而LDT又由LDTR确定。如果采用分页机制,那么由线性地址空间到物理地址空间的映射由包含页目录表起始物理地址的控制寄存器CR3确定。所以,与特定任务相关的虚拟地址空间到物理地址空间的映射由LDTR和CR3确定。显然,随着任务的切换,地址映射关系也要切换。
      TSS的地址映射寄存器区域由位于偏移1CH处的双字字段(CR3)和位于偏移60H处的字字段(LDTR)组成。在任务切换时,处理器自动从要执行任务的TSS中取出这两个字段,分别装入到寄存器CR3和LDTR。这样就改变了虚拟地址空间到物理地址空间的映射。
      但是,在任务切换时,处理器并不把换出任务但是的寄存器CR3和LDTR的内容保存到TSS中的地址映射寄存器区域。事实上,处理器也从来不向该区域自动写入。因此,如果程序改变了LDTR或CR3,那么必须把新值人为地保存到TSS中的地址映射寄存器区域相应字段中。可以通过别名技术实现此功能。

    4.链接字段

      链接字段安排在TSS内偏移0开始的双字中,其高16位未用。在起链接作用时,地16位保存前一任务的TSS描述符的选择子。
      如果当前的任务由段间调用指令CALL或中断/异常而激活,那么链接字段保存被挂起任务的 TSS的选择子,并且标志寄存器EFLAGS中的NT位被置1,使链接字段有效。在返回时,由于NT标志位为1,返回指令RET或中断返回指令IRET将使得控制沿链接字段所指恢复到链上的前一个任务。

    5.其它字段

      为了实现输入/输出保护,要使用I/O许可位图。任务使用的I/O许可位图也存放在TSS中,作为TSS的扩展部分。在TSS内偏移66H处的字用于存放I/O许可位图在TSS内的偏移(从TSS开头开始计算)。关于I/O许可位图的作用,以后的文章中将会详细介绍。
      在TSS内偏移64H处的字是为任务提供的特别属性。在80386中,只定义了一种属性,即调试陷阱。该属性是字的最低位,用T表示。该字的其它位置被保留,必须被置为0。在发生任务切换时,如果进入任务的T位为1,那么在任务切换完成之后,新任务的第一条指令执行之前产生调试陷阱。

    6.用结构类型定义TSS

      根据上图给出的任务状态段TSS的结构,可定义如下的TSS结构类型:
    ;----------------------------------------------------------------------------
    ;任务状态段结构类型定义
    ;----------------------------------------------------------------------------
    TSS             STRUC
    
    TRLink          DW      0      ;链接字段
                    DW      0      ;不使用,置为0
    TRESP0          DD      0      ;0级堆栈指针
    
    TRSS0           DW      0      ;0级堆栈段寄存器
                    DW      0      ;不使用,置为0
    TRESP1          DD      0      ;1级堆栈指针
    
    TRSS1           DW      0      ;1级堆栈段寄存器
                    DW      0      ;不使用,置为0
    TRESP2          DD      0      ;2级堆栈指针
    
    TRSS2           DW      0      ;2级堆栈段寄存器
                    DW      0      ;不使用,置为0
    TRCR3           DD      0      ;CR3
    
    TREIP           DD      0      ;EIP
    TREFlag         DD      0      ;EFLAGS
    TREAX           DD      0      ;EAX
    
    TRECX           DD      0      ;ECX
    TREDX           DD      0      ;EDX
    TREBX           DD      0      ;EBX
    
    TRESP           DD      0      ;ESP
    TREBP           DD      0      ;EBP
    TRESI           DD      0      ;ESI
    
    TREDI           DD      0      ;EDI
    TRES            DW      0      ;ES
                    DW      0      ;不使用,置为0
    
    TRCS            DW      0      ;CS
                    DW      0      ;不使用,置为0
    TRSS            DW      0      ;SS
    
                    DW      0      ;不使用,置为0
    TRDS            DW      0      ;DS
                    DW      0      ;不使用,置为0
    
    TRFS            DW      0      ;FS
                    DW      0      ;不使用,置为0
    TRGS            DW      0      ;GS
    
                    DW      0      ;不使用,置为0
    TRLDTR          DW      0      ;LDTR
                    DW      0      ;不使用,置为0
    TRTrip          DW      0      ;调试陷阱标志(只用位0)
    TRIOMap         DW      $ 2    ;指向I/O许可位图区的段内偏移
    TSS             ENDS
    

    转载于:https://www.cnblogs.com/guanlaiy/archive/2012/10/25/2738355.html

    展开全文
  • 任务状态段TSS

    2017-05-02 15:28:42
    任务状态段 不要被名字所吓倒,它不过是一块位于内存中的结构体而已。有一点需要注意的是,不要把它和任务切换关联起来(切记),否则你会被搞晕,它只是位于内存中的一段数据。 Intel 白皮书给出TSS在内存中的图...

    任务状态段

    不要被名字所吓倒,它不过是一块位于内存中的结构体而已。有一点需要注意的是,不要把它和任务切换关联起来(切记),否则你会被搞晕,它只是位于内存中的一段数据。

    Intel 白皮书给出TSS在内存中的图是这样的,它保存了一些重要的值。


    这里写图片描述

    抽象成结构体就是下面这个样子。
    typedef struct TSS {
        DWORD link; // 保存前一个 TSS 段选择子,使用 call 指令切换寄存器的时候由CPU填写。
        // 这 6 个值是固定不变的,用于提权,CPU 切换栈的时候用
        DWORD esp0; // 保存 0 环栈指针
        DWORD ss0;  // 保存 0 环栈段选择子
        DWORD esp1; // 保存 1 环栈指针
        DWORD ss1;  // 保存 1 环栈段选择子
        DWORD esp2; // 保存 2 环栈指针
        DWORD ss2;  // 保存 2 环栈段选择子
        // 下面这些都是用来做切换寄存器值用的,切换寄存器的时候由CPU自动填写。
        DWORD cr3; 
        DWORD eip;  
        DWORD eflags;
        DWORD eax;
        DWORD ecx;
        DWORD edx;
        DWORD ebx;
        DWORD esp;
        DWORD ebp;
        DWORD esi;
        DWORD edi;
        DWORD es;
        DWORD cs;
        DWORD ss;
        DWORD ds;
        DWORD fs;
        DWORD gs;
        DWORD ldt;
        // 这个暂时忽略
        DWORD io_map;
    } TSS;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在学习调用门,中断门和陷阱门已经知道,代码发生提权的时候,是需要切换栈的。

    之前遗留的一个问题是,栈段描述符和栈顶指针从哪里来?那时只是简单的讲了一下是从 TSS 中来的。

    如果代码从3环跨到0环,现在观察上面的图或者结构体,可以看到确实存在这么一个 SS0 和 ESP0。提权的时候,CPU就从这个TSS里把SS0和ESP0取出来,放到 ss 和 esp 寄存器中。

    CPU怎么找到TSS

    前面已经知道,CPU可以通过 gdtr 寄存器来知道 GDT表在哪里,通过 idtr 寄存器知道 IDT 表在哪里。实际上,CPU是通过 tr 寄存器来确定 TSS 的位置的。

    和 gdtr,idtr 这些不同的是,tr 寄存器是段寄存器,之前已经知道的段寄存器有 cs, ds, es, ss, fs, gs 。也知道段寄存器有96位,还做过实验验证。tr 寄存器中存放的就是描述了TSS段的相关信息,比如TSS段的基址,大小和属性。

    可以通过 ltr指令跟上TSS段描述符的选择子来加载TSS段。该指令是特权指令,只能在特权级为0的情况下使用。

    TSS 段描述符

    |   7    |     6       |     5     |   4    |   3    |   2    |   1    |   0    |  字节
    |76543210|7 6 5 4 3210 |7 65 4 3210|76543210|76543210|76543210|76543210|76543210|  比特
    |--------|-|-|-|-|---- |-|--|-|----|--------|--------|--------|--------|--------|  占位
    |  BASE  |G|D|0|A|LIMIT|P|D |S|TYPE|<------- BASE 23-0 ------>|<-- LIMIT 15-0 ->|  含义
    |  31-24 | |/| |V|19-16| |P |
               |B| |L|     | |L |
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当S=0, TYPE=1001或者TYPE=1011的时候,表示这是一个TSS段描述符。当TSS段没被加载进 tr 寄存器时,TYPE=1001,一旦TSS被加载进 tr 寄存器,TYPE就变成了1011.

    TSS的用途

    • 保存0环、1环和2环的栈段选择子和栈顶指针

    前面讲到了,在跨段提权的时候,需要切换栈,CPU会通过 tr 寄存器找到 TSS,取出其中的 SS0 和 ESP0 复制到 ss 和 esp 寄存器中。这只是 TSS 的一个用途,也是现代 Windows 操作系统使用到的功能。

    • 一次性切换一堆寄存器

    TSS 的另一个用途是什么?通过观察 TSS 的结构还发现 TSS 不仅存储了不同特权级下的 SS 和 ESP,还有 cs, esp, ss, esp 等等,这些后面不带数字的变量名,有着各自的用途。可以通过 call/jmp + TSS段选择子指令一次性把这些值加载到 CPU 对应的寄存器中。同时,旧值将保存在旧的 TSS 中。

    GDT 表中可以存放多个TSS描述符,这意味着内存中可以存在多份不同的TSS。总有一个 TSS 是在当前使用中的,也就是 tr 寄存器指向的那个 TSS。当使用 call/jmp + TSS段选择子的时候,CPU做了以下几件事情。

    1. 把当前所有寄存器(TSS结构中有的那些寄存器)的值填写到当前 tr 段寄存器指向的 TSS 中
    2. 把新的 TSS 段选择子指向的段描述符加载到 tr 段寄存器中
    3. 把新的 TSS 段中的值覆盖到当前所有寄存器(TSS结构中有的那些寄存器)中

    总结

    本节主要讲了 TSS 的两个功能:

    • 提权时栈切换用到了 TSS
    • 切换一堆寄存器

    本文始终没有把 TSS 和任务切换关联起来,只是为了避免给初学者造成困扰。虽然 Intel 设计的初衷是用它来做任务切换,然而,在现代操作系统中(无论是 Windows 还是 Linux),都没有使用这种方式来执行任务切换,比如线程切换和进程切换。主要原因是这种切换速度非常慢,一条指令要消耗200多个时钟周期。

    至于现代操作系统如何进行线程或进程的切换,这是以后要讲的事情。

    展开全文
  • 任务状态段(Task-state segment, TSS) 之前讲到了,因为SS段的CPL必须与CS段的CPL一致,所以当使用调用门、中断门或陷阱门并产生权限切换时,必然会引起堆栈的切换。通过门描述符的学习可以知道CPL切换时,CS由门...

    任务状态段(Task-state segment, TSS)
    之前讲到了,因为SS段的CPL必须与CS段的CPL一致,所以当使用调用门、中断门或陷阱门并产生权限切换时,必然会引起堆栈的切换。通过门描述符的学习可以知道CPL切换时,CS由门描述符决定,但是新的SS和ESP由何而来的呢?答案是 任务状态段(Task-state segment, TSS)

    任务状态段是一块大小为104字节的内存,它保存了一些重要的值
    图在手册中7.2.1 Task-State Segment (TSS)
    在这里插入图片描述
    抽象成结构体就是下面这个样子。

    typedef struct TSS {
        DWORD link; // 保存前一个 TSS 段选择子,使用 call 指令切换寄存器的时候由CPU填写。
        // 这 6 个值是固定不变的,用于提权,CPU 切换栈的时候用
        DWORD esp0; // 保存 0 环栈指针
        DWORD ss0;  // 保存 0 环栈段选择子
        DWORD esp1; // 保存 1 环栈指针
        DWORD ss1;  // 保存 1 环栈段选择子
        DWORD esp2; // 保存 2 环栈指针
        DWORD ss2;  // 保存 2 环栈段选择子
        // 下面这些都是用来做切换寄存器值用的,切换寄存器的时候由CPU自动填写。
        DWORD cr3; 
        DWORD eip;  
        DWORD eflags;
        DWORD eax;
        DWORD ecx;
        DWORD edx;
        DWORD ebx;
        DWORD esp;
        DWORD ebp;
        DWORD esi;
        DWORD edi;
        DWORD es;
        DWORD cs;
        DWORD ss;
        DWORD ds;
        DWORD fs;
        DWORD gs;
        DWORD ldt;
        // I/O 位图基地址域 
        DWORD io_map;
    } TSS;
    

    手册里对每个变量的解释是这样的

    The processor updates dynamic fields when a task is suspended during a task switch. The following are dynamic
    fields:
    • General-purpose register fields — State of the EAX, ECX, EDX, EBX, ESP, EBP, ESI, and EDI registers prior
    to the task switch.
    • Segment selector fields — Segment selectors stored in the ES, CS, SS, DS, FS, and GS registers prior to the
    task switch.
    • EFLAGS register field — State of the EFAGS register prior to the task switch.
    • EIP (instruction pointer) field — State of the EIP register prior to the task switch.
    • Previous task link field — Contains the segment selector for the TSS of the previous task (updated on a task
    switch that was initiated by a call, interrupt, or exception). This field (which is sometimes called the back link
    field) permits a task switch back to the previous task by using the IRET instruction.
    The processor reads the static fields, but does not normally change them. These fields are set up when a task is
    created. The following are static fields:
    • LDT segment selector field — Contains the segment selector for the task’s LDT.
    CR3 control register field — Contains the base physical address of the page directory to be used by the task.
    Control register CR3 is also known as the page-directory base register (PDBR).
    • Privilege level-0, -1, and -2 stack pointer fields — These stack pointers consist of a logical address made
    up of the segment selector for the stack segment (SS0, SS1, and SS2) and an offset into the stack (ESP0,
    ESP1, and ESP2). Note that the values in these fields are static for a particular task; whereas, the SS and ESP
    values will change if stack switching occurs within the task.
    • T (debug trap) flag (byte 100, bit 0) — When set, the T flag causes the processor to raise a debug exception
    when a task switch to this task occurs (see Section 17.3.1.5, “Task-Switch Exception Condition”).
    • I/O map base address field — Contains a 16-bit offset from the base of the TSS to the I/O permission bit
    map and interrupt redirection bitmap. When present, these maps are stored in the TSS at higher addresses.
    The I/O map base address points to the beginning of the I/O permission bit map and the end of the interrupt
    redirection bit map. See Chapter 14, “Input/Output,” in the Intel® 64 and IA-32 Architectures Software
    Developer’s Manual, Volume 1, for more information about the I/O permission bit map. See Section 20.3,
    “Interrupt and Exception Handling in Virtual-8086 Mode,” for a detailed description of the interrupt redirection
    bit map.
    If paging is used:
    • Avoid placing a page boundary in the part of the TSS that the processor reads during a task switch (the first 104
    bytes). The processor may not correctly perform address translations if a boundary occurs in this area. During
    a task switch, the processor reads and writes into the first 104 bytes of each TSS (using contiguous physical
    addresses beginning with the physical address of the first byte of the TSS). So, after TSS access begins, if part
    of the 104 bytes is not physically contiguous, the processor will access incorrect information without generating
    a page-fault exception.
    • Pages corresponding to the previous task’s TSS, the current task’s TSS, and the descriptor table entries for
    each all should be marked as read/write.
    • Task switches are carried out faster if the pages containing these structures are present in memory before the
    task switch is initiated

    通过观察这个结构体可以看到有两个DWORD变量叫ss0和esp0,当我们将代码从三环跨到0环时,CPU就会从这个TSS里把SS0和ESP0取出来,放到 ss 和 esp 寄存器中,当然这里windows 只用了0和3

    TSS的作用
    保存0环、1环和2环的栈段选择子和栈顶指针
    前面讲到了,在跨段提权的时候,需要切换栈,CPU会通过 tr 寄存器找到 TSS,取出其中的 SS0 和 ESP0 复制到 ss 和 esp 寄存器中。这只是 TSS 的一个用途,也是现代 Windows 操作系统使用到的功能。

    一次性切换一堆寄存器
    TSS 的另一个用途是什么?通过观察 TSS 的结构还发现 TSS 不仅存储了不同特权级下的 SS 和 ESP,还有 cs, esp, ss, esp 等等,这些后面不带数字的变量名,有着各自的用途。可以通过 call/jmp + TSS段选择子指令一次性把这些值加载到 CPU 对应的寄存器中。同时,旧值将保存在旧的 TSS 中。

    GDT 表中可以存放多个TSS描述符,这意味着内存中可以存在多份不同的TSS。总有一个 TSS 是在当前使用中的,也就是 tr 寄存器指向的那个 TSS。当使用 call/jmp + TSS段选择子的时候,CPU做了以下几件事情。
    把当前所有寄存器(TSS结构中有的那些寄存器)的值填写到当前 tr 段寄存器指向的 TSS 中
    把新的 TSS 段选择子指向的段描述符加载到 tr 段寄存器中
    把新的 TSS 段中的值覆盖到当前所有寄存器(TSS结构中有的那些寄存器)中

    图下是Intel的设计思想,但是现代操作系统中并没有完全这样用
    在这里插入图片描述
    在这里插入图片描述

    TR段寄存器

    那么,CPU如何找到TSS段呢?
    CPU可以通过 gdtr 寄存器来知道 GDT表在哪里,通过 idtr 寄存器知道 IDT 表在哪里。同样的,CPU通过 tr 寄存器来确定 TSS 的位置。但是! tr寄存器是不同于 gdtr和 idtr 的,tr寄存器在分类上是属于 段寄存器,是长度为96位的段寄存器。TR寄存器的值是当操作系统启动时,就从GDT表 中TSS段描述符中加载出来的,TSS段描述符存储在GDT表中

    7.2.4 Task Register
    The task register holds the 16-bit segment selector and the entire segment descriptor (32-bit base address (64 bits
    in IA-32e mode), 16-bit segment limit, and descriptor attributes) for the TSS of the current task (see Figure 2-6).
    This information is copied from the TSS descriptor in the GDT for the current task. Figure 7-5 shows the path the
    processor uses to access the TSS (using the information in the task register).
    The task register has a visible part (that can be read and changed by software) and an invisible part (maintained
    by the processor and is inaccessible by software). The segment selector in the visible portion points to a TSS
    descriptor in the GDT. The processor uses the invisible portion of the task register to cache the segment descriptor
    for the TSS. Caching these values in a register makes execution of the task more efficient. The LTR (load task
    register) and STR (store task register) instructions load and read the visible portion of the task register:
    The LTR instruction loads a segment selector (source operand) into the task register that points to a TSS descriptor
    in the GDT. It then loads the invisible portion of the task register with information from the TSS descriptor. LTR is a
    privileged instruction that may be executed only when the CPL is 0. It’s used during system initialization to put an
    initial value in the task register. Afterwards, the contents of the task register are changed implicitly when a task
    switch occurs.
    The STR (store task register) instruction stores the visible portion of the task register in a general-purpose register
    or memory. This instruction can be executed by code running at any privilege level in order to identify the currently
    running task. However, it is normally used only by operating system software.
    On power up or reset of the processor, segment selector and base address are set to the default value of 0; the limit
    is set to FFFFH.

    在这里插入图片描述

    说到这里又有一个新概念了

    就是TSS段描述符

    The TSS, like all other segments, is defined by a segment descriptor. Figure 7-3 shows the format of a TSS
    descriptor. TSS descriptors may only be placed in the GDT; they cannot be placed in an LDT or the IDT.
    An attempt to access a TSS using a segment selector with its TI flag set (which indicates the current LDT) causes
    a general-protection exception (#GP) to be generated during CALLs and JMPs; it causes an invalid TSS exception
    (#TS) during IRETs. A general-protection exception is also generated if an attempt is made to load a segment
    selector for a TSS into a segment register.
    The busy flag (B) in the type field indicates whether the task is busy. A busy task is currently running or suspended.
    A type field with a value of 1001B indicates an inactive task; a value of 1011B indicates a busy task. Tasks are not
    recursive. The processor uses the busy flag to detect an attempt to call a task whose execution has been interrupted. To insure that there is only one busy flag is associated with a task, each TSS should have only one TSS
    descriptor that points to it

    在这里插入图片描述

    The base, limit, and DPL fields and the granularity and present flags have functions similar to their use in datasegment descriptors (see Section 3.4.5, “Segment Descriptors”). When the G flag is 0 in a TSS descriptor for a 32-
    bit TSS, the limit field must have a value equal to or greater than 67H, one byte less than the minimum size of a
    TSS. Attempting to switch to a task whose TSS descriptor has a limit less than 67H generates an invalid-TSS exception (#TS). A larger limit is required if an I/O permission bit map is included or if the operating system stores additional data. The processor does not check for a limit greater than 67H on a task switch; however, it does check
    when accessing the I/O permission bit map or interrupt redirection bit map.
    Any program or procedure with access to a TSS descriptor (that is, whose CPL is numerically equal to or less than
    the DPL of the TSS descriptor) can dispatch the task with a call or a jump.
    In most systems, the DPLs of TSS descriptors are set to values less than 3, so that only privileged software can
    perform task switching. However, in multitasking applications, DPLs for some TSS descriptors may be set to 3 to
    allow task switching at the application (or user) privilege level.

    当然64位的TSS段描述符,这里不展开说
    TSS段描述符是系统段描述符中的一种,这里注意一点的是B位,如果为0则表示没有加载到TR寄存器里,如果为1则表示已经加载到TR寄存器中

    TR寄存器读写

    for the TSS. Caching these values in a register makes execution of the task more efficient. The LTR (load task
    register) and STR (store task register) instructions load and read the visible portion of the task register:
    The LTR instruction loads a segment selector (source operand) into the task register that points to a TSS descriptor
    in the GDT. It then loads the invisible portion of the task register with information from the TSS descriptor. LTR is a
    privileged instruction that may be executed only when the CPL is 0. It’s used during system initialization to put an
    initial value in the task register. Afterwards, the contents of the task register are changed implicitly when a task
    switch occurs.
    The STR (store task register) instruction stores the visible portion of the task register in a general-purpose register
    or memory. This instruction can be executed by code running at any privilege level in order to identify the currently
    running task. However, it is normally used only by operating system software.

    这里注意一点的是LTR只改变TR寄存器,并没有改变TSS,并且只能在系统层使用,加载后TSS段描述符的状态位会发生改变(B位)

    下面我们做个小实验
    在R3,我们可以通过JMP FAR 或者CALL FAR 去访问任务段
    在这里插入图片描述
    这里简单翻译一下如果JMP FAR或者 CALL FAR 是一个TSS段的话
    1.将当前任务的状态存储在当前TSS中
    2.装载新任务段选择子到TR寄存器中。
    3.通过GDT中的段描述符访问新的TSS
    4.将新 TSS 中新任务的状态装载到通用寄存器、段寄存器、LDTR、控制寄存器 CR3(页表基地址)、EFLAGS 寄存器和 EIP 寄存器中。
    5.开始执行新任务

    首先我们来构造一个TSS段描述符
    假设地址0x0041E000

    Base 31:24  G     AVL  Limit19:16  P   DPL     Type   Base 23:16        
    000000000 00  0    00001110  1001   0100 0001
    Base Address 15:00      Segment Limit 15:001110 0000 0000 0000‬     ‭ 0000 0000 01101000
    

    写入程序如下

    #include <iostream>
    #include <windows.h>
    
    
    
    
    typedef struct TSS {
    	DWORD link; // 保存前一个 TSS 段选择子,使用 call 指令切换寄存器的时候由CPU填写。
    	// 这 6 个值是固定不变的,用于提权,CPU 切换栈的时候用
    	DWORD esp0; // 保存 0 环栈指针
    	DWORD ss0;  // 保存 0 环栈段选择子
    	DWORD esp1; // 保存 1 环栈指针
    	DWORD ss1;  // 保存 1 环栈段选择子
    	DWORD esp2; // 保存 2 环栈指针
    	DWORD ss2;  // 保存 2 环栈段选择子
    	// 下面这些都是用来做切换寄存器值用的,切换寄存器的时候由CPU自动填写。
    	DWORD cr3;
    	DWORD eip;
    	DWORD eflags;
    	DWORD eax;
    	DWORD ecx;
    	DWORD edx;
    	DWORD ebx;
    	DWORD esp;
    	DWORD ebp;
    	DWORD esi;
    	DWORD edi;
    	DWORD es;
    	DWORD cs;
    	DWORD ss;
    	DWORD ds;
    	DWORD fs;
    	DWORD gs;
    	DWORD ldt;
    	// I/O 位图基地址域 
    	DWORD io_map;
    } TSS;
    
    char st[10] = { 0 }; // 0041e924
    
    DWORD g_esp;
    DWORD g_cs;
    
    TSS tss = {
    		0x00000000,//link
    		(DWORD)st,//esp0
    		0x00000010,//ss0
    		0x00000000,//esp1
    		0x00000000,//ss1
    		0x00000000,//esp2
    		0x00000000,//ss2
    		0x00000000,//cr3
    		0x0040fad0,//eip
    		0x00000000,//eflags
    		0x00000000,//eax
    		0x00000000,//ecx
    		0x00000000,//edx
    		0x00000000,//ebx
    		(DWORD)st,//esp
    		0x00000000,//ebp
    		0x00000000,//esi
    		0x00000000,//edi
    		0x00000023,//es  
    		0x00000008,//cs  
    		0x00000010,//ss
    		0x00000023,//ds
    		0x00000030,//fs
    		0x00000000,//gs
    		0x00000000,//ldt
    		0x20ac0000
    };
    
      __declspec(naked) void func() {
    	__asm {
    		mov g_esp, esp;
    		mov eax, 0;
    		mov ax, cs;
    		mov g_cs, eax;
    
    		iret;  // 这个地方需要注意,如果使用 call ,这里就填写 iret。
    	}
    }
    
    
    int main(int argc, char* argv[])
    {
    	tss.eip = (DWORD)func;
    	printf("TSS:%x\n", &tss);
    	printf("func:%x\n", tss.eip);
    	printf("please input cr3:\n");
    	scanf("%x", &(tss.cr3));
    	
    	char buffer[6] = { 0, 0, 0, 0, 0x48, 0 };
    	__asm {
    		call fword ptr[buffer]
    	}
    
    	// 不出意外,这里打印的值分别是 00000008, 0041e924
    	printf("g_cs = %08x\ng_esp = %08x\n", g_cs, g_esp);
    	return 0;
    }
    

    显示TSS位置后构造TSS段寄存器
    在这里插入图片描述
    修改GDT表项
    在这里插入图片描述

    进入windbg下断,输入!process 0 0找到相应进程cr3,记下,也就是DirBase

    在这里插入图片描述
    输入完成后继续运行成功执行读取代码

    在这里插入图片描述

    PS:
    这里有个小坑,如果执行代码里写入int3,不恢复eflags寄存器,就会卡死
    原因是
    因为当进行TSS跳转时,其会将老的TSS保存在新的TSS头部(上面我们看到),当我们使用iretd返回时,其不是像中断那样根据返回地址,而是根据TSS段选择子找旧的TSS段内存,然后将里面的寄存器全部加载进去。
    而INT 3 会清空 VM、NT、IF、TF四个位,其中NT表示嵌套任务段(nested task),如果清空,其就认为不存在任务段嵌套,直接像常规那样,根据返回地址返回,此时就会出错。
    因此在返回前就会存在下面一段代码来修改elfags寄存器中的NT位。

    // reset the NT flag in eflags register
    __asm{
     	pushfd;
        pop eax;
        or eax,0x4000;
        push eax;
     	popfd;
    }
    
    展开全文
  • TSS 任务状态段

    2014-09-18 17:44:00
    TSS(任务状态段) 1 什么是TSS TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。 2 TSS工作细节 TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和...
    1 什么是TSS  
    TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。 

     
    2 TSS工作细节
    TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到TR(任务寄存器)所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换。
    3 TSS的格式 任务状态段TSS的基本格式如下图所示。 TSS的基本格式由104字节组成。这104字节的基本格式是不可改变的,但在此之外系统软件还可定义若干附加信息。基本的104字节可分为链接字段区域、内层堆栈指针区域、地址映射寄存器区域、寄存器保存区域和其它字段等五个区域。
    (1). 寄存器保存区域 寄存器保存区域位于TSS内偏移20H至5FH处,用于保存通用寄存器、段寄存器、指令指针和标志寄存器。当TSS对应的任务正在执行时,保存区域是未定义的;在当前任务被切换出时,这些寄存器的当前值就保存在该区域。当下次切换回原任务时,再从保存区域恢复出这些寄存器的值,从而,使处理器恢复成该任务换出前的状态,最终使任务能够恢复执行。 从上图可见,各通用寄存器对应一个32位的双字,指令指针和标志寄存器各对应一个32位的双字;各段寄存器也对应一个32位的双字,段寄存器中的选择子只有16位,安排再双字的低16位,高16位未用,一般应填为0。
    (2). 内层堆栈指针区域 为了有效地实现保护,同一个任务在不同的特权级下使用不同的堆栈。例如,当从外层特权级3变换到内层特权级0时,任务使用的堆栈也同时从3级变换到0级堆栈;当从内层特权级0变换到外层特权级3时,任务使用的堆栈也同时从0级堆栈变换到3级堆栈。所以,一个任务可能具有四个堆栈,对应四个特权级。四个堆栈需要四个堆栈指针。 TSS的内层堆栈指针区域中有三个堆栈指针,它们都是48位的全指针(16位的选择子和32位的偏移),分别指向0级、1级和2级堆栈的栈顶,依次存放在TSS中偏移为4、12及20开始的位置。当发生向内层转移时,把适当的堆栈指针装入SS及ESP寄存器以变换到内层堆栈,外层堆栈的指针保存在内层堆栈中。没有指向3级堆栈的指针,因为3级是最外层,所以任何一个向内层的转移都不可能转移到3级。 但是,当特权级由内层向外层变换时,并不把内层堆栈的指针保存到TSS的内层堆栈指针区域。实际上,处理器从不向该区域进行写入,除非程序设计者认为改变该区域的值。这表明向内层转移时,总是把内层堆栈认为是一个空栈。因此,不允许发生同级内层转移的递归,一旦发生向某级内层的转移,那么返回到外层的正常途径是相匹配的向外层返回。
    (3). 地址映射寄存器区域 从虚拟地址空间到线性地址空间的映射由GDT和LDT确定,与特定任务相关的部分由LDT确定,而LDT又由LDTR确定。如果采用分页机制,那么由线性地址空间到物理地址空间的映射由包含页目录表起始物理地址的控制寄存器CR3确定。所以,与特定任务相关的虚拟地址空间到物理地址空间的映射由LDTR和CR3确定。显然,随着任务的切换,地址映射关系也要切换。 [Page] TSS的地址映射寄存器区域由位于偏移1CH处的双字字段(CR3)和位于偏移60H处的字字段(LDTR)组成。在任务切换时,处理器自动从要执行任务的TSS中取出这两个字段,分别装入到寄存器CR3和LDTR。这样就改变了虚拟地址空间到物理地址空间的映射。 但是,在任务切换时,处理器并不把换出任务但是的寄存器CR3和LDTR的内容保存到TSS中的地址映射寄存器区域。事实上,处理器也从来不向该区域自动写入。因此,如果程序改变了LDTR或CR3,那么必须把新值人为地保存到TSS中的地址映射寄存器区域相应字段中。可以通过别名技术实现此功能。
    (4). 链接字段 链接字段安排在TSS内偏移0开始的双字中,其高16位未用。在起链接作用时,地16位保存前一任务的TSS描述符的选择子。 如果当前的任务由段间调用指令CALL或中断/异常而激活,那么链接字段保存被挂起任务的 TSS的选择子,并且标志寄存器EFLAGS中的NT位被置1,使链接字段有效。在返回时,由于NT标志位为1,返回指令RET或中断返回指令IRET将使得控制沿链接字段所指恢复到链上的前一个任务。
    (5). 其它字段 为了实现输入/输出保护,要使用I/O许可位图。任务使用的I/O许可位图也存放在TSS中,作为TSS的扩展部分。在TSS内偏移66H处的字用于存放I/O许可位图在TSS内的偏移(从TSS开头开始计算)。关于I/O许可位图的作用,以后的文章中将会详细介绍。 在TSS内偏移64H处的字是为任务提供的特别属性。在80386中,只定义了一种属性,即调试陷阱。该属性是字的最低位,用T表示。该字的其它位置被保留,必须被置为0。在发生任务切换时,如果进入任务的T位为1,那么在任务切换完成之后,新任务的第一条指令执行之前产生调试陷阱。

    -----------------------------------------------------------------------------------------------------------

    我看了你说的两段了,是P393的吧
    第一段是这样理解:
      当从外层转移到内层时系统会把返回地址和外层堆栈的指针保留到内层堆栈中,比如:从R3调用R0的服务,CPU会把R3的堆栈、返回地址都压入R0的堆栈中,以备返回时使用。

    第二段:
      所谓“一旦发生向某级内层转移,那么返回到外层的正常途径是相匹配的向外层返回”。意思是说,从外层到内层,可以通过CALL/JMP来直接转移,但从内层到外层,不能直接通过CALL/JMP,只能通过远程RET,这样做的原因是为了确保内层的堆栈不被外层代码得知,而不是“这表明向内层转移时,总是把内层堆栈认为是一个空栈”。
    至于递归调用那个说法,我也不是很明白

     

    http://bbs.csdn.net/topics/90016769

     

    转载于:https://www.cnblogs.com/wanghj-dz/p/3979729.html

    展开全文
  • 13-任务状态段(TSS)

    万次阅读 2016-09-24 15:22:14
    任务状态段不要被名字所吓倒,它不过是一块位于内存中的结构体而已。有一点需要注意的是,不要把它和任务切换关联起来(切记),否则你会被搞晕,它只是位于内存中的一段数据。Intel 白皮书给出TSS在内存中的图是...
  • x86保护模式 任务状态段和控制门 每个任务都有一个任务状态段TSS 用于保存任务的有关信息 在任务内权变和任务切换时 需要用到这些信息 任务内权变的转移和任务切换 一般需要通过控制门进行这些转移。 <一>...
  • TR寄存器和TSS任务状态段数据结构可以帮助Linux操作系统快速的完成任务切换并保存原有任务的内容, 具体的过程分析如下:  任务状态段TSS 先来看下任务状态段TSS的结构: 如上图所示:整个TSS可分成两类: ...
  • 线程上下文切换时TSS任务状态段作用分析 1 什么是TSS TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程/线程)切换时保存任务线程信息。 任务状态段TSS的结构 TSS段的最小长度为104字节,低...
  • 1 任务状态段(TSS) 任务状态段(Task-State Segment(TSS)),保存任务状态信息的系统段为任务状态段。图7-2描述32位CPU的TSS信息。TSS主要分为动态字段和静态字段。 在任务切换过程中当任务挂起时,处理器会...
  • 任务状态段(Task State Segment)

    千次阅读 2013-04-27 09:02:40
    任务状态段(Task State Segment)是保存一个任务重要信息的特殊段...任务状态段寄存器TR的可见部分含有当前任务的任务状态段描述符的选择子,TR的不可见的高速缓冲寄存器部分含有当前任务状态段的段基地址和段界限等
  • 任务状态段和门

    千次阅读 2010-10-14 14:08:00
     每一个任务(可以理解为进程)都有一个任务状态段TSS,用于保存任务的有关信息,在任务内变换特权级和任务切换时要用到这些信息。  系统段是为实现存储管理机制所使用的一种特别的段。在80386中,有两...
  • 任务状态段 控制门

    2009-11-28 01:16:00
    任务状态段 控制门 顺子 发表于2009年11月28日 01:02 阅读(0) 评论(0) 分类: Linux内核 权限: 公开 每个任务有一个任务状态段TSS,用于保存任务的有关信息,在任务内变换特权级和任务切换时,要用到这些信息。...
  • 任务状态段和控制门

    2006-03-24 09:23:00
    每个任务有一个任务状态段TSS,用于保存任务的有关信息,在任务内变换特权级和任务切换时,要用到这些信息。为了控制任务内发生特权级变换的转移,为了控制任务切换,一般要通过控制门进行这些转移。本文将介绍任务...
  • 7.1 Task State Segment任务状态段 All the information the processor needs in order to manage a task is stored in a special type of segment, a task state segment (TSS).Figure 7-1shows the format of a ...
  • 在一个多任务环境中,当发生了任务切换,需保护现场,因此每个任务的应当用一个额外的内存区域保存相关信息,即任务状态段(TSS);TSS格式固定,104个字节,处理器固件能识别TSS中元素,并在任务切换时读取其中信息。...
  • TSS 任务状态段 详解

    2014-09-22 09:50:00
    http://blog.163.com/di_yang@yeah/blog/static/86118492201222210725146/1 什么是TSS TSS 全称task... 2 TSS工作细节 TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正...
  • TSS(任务状态段)

    千次阅读 2016-09-03 16:12:53
    TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。  2 TSS工作细节 TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂...
  • 浅析linux内核调度器与时间系统之任务状态段 作者:李万鹏
  • TSS 段(任务状态段

    2010-11-04 22:27:00
    TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的...由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换。...
  • 关于利用任务状态段TSS进行进程切换的一点说明:由于在时钟中断里进行任务切换,也就是用JMP指令跳转到新的任务中去,这时的时钟中断是屏蔽的,但任务切换是直接进入新任务,没有将时钟中断屏蔽打开,所以造成了任务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,627
精华内容 1,050
关键字:

任务状态段