[转](15)TSS,TR寄存器,TSS描述符,任务段跳转实验

2023-10-12 16:30

本文主要是介绍[转](15)TSS,TR寄存器,TSS描述符,任务段跳转实验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

一、TSS,TR寄存器,TSS门描述符的关系

 

首先,通过下图,了解 TSS,TR寄存器,TSS描述符的关系:

 

在这里插入图片描述

 

TSS(Task-state segment)是一块104字节的内存,用于存储大部分寄存器的值;

 

在这里插入图片描述
TSS设计出来的目的是任务切换,或者说是一次性替换一大堆寄存器。

 

TR寄存器存储了TSS的地址,大小,和TSS门描述符选择子;
在这里插入图片描述

 

TSS描述符是GDT表中的一项,操作系统启动时,从门描述符取值初始化TR寄存器。

 

在这里插入图片描述

eq 8003f048    xx00e9xx xxxx0068 

其中 xx xx xxxx 32个字节是TSS内存的地址  也就是自己申请的地址

00e9是 0000 0000 1110 1001 第二个0000 Limit 是(104个字节)起始地址

0068是 segment Limit   =104个字节

发现这里的DPL是3  下面代码0048 或者004B  CPL=0 还是 CPL=3 都可以成功 

测试 e=1110   DPL必须为3时  才可以正常执行  是可以提权读取高2G

说明提权0环是由TTS赋值的CS  SS决定  和DPL CPL无关   

测试将TTS CS  SS  设置成3环的  能不能读取高两G  系统崩溃!

 

二、LTR STR 指令

 

LTR 指令是0环指令,格式如下:

 

mov ax,SelectorTSS
ltr ax

 

执行该指令,从GDT表取TSS描述符填充TR寄存器,但并不会修改其他寄存器。
执行指令后,TSS描述符TYPE域低2位会置1. (1001->1011)当成功将 TSS descriptor 加载到 TR 寄存器后,processor 将 available TSS 置为 busy TSS,在返回时将 busy TSS 置为 available TSS。

 

STR 指令只会读取 TR 的16位选择子部分,该指令没有特权要求。指令格式如下:

 

str ax

 

三、TSS

 

在这里插入图片描述

 

Windows只使用了TSS的SS0和ESP0,用于权限切换。
TSS这个东西是Intel设计出来做任务切换的,windows和linux都没有使用任务,而是自己实现了线程。在windows中,TSS唯一的作用就是权限切换时要用到SS0和ESP0,又或者这样理解,TSS就是用来一次性替换一堆寄存器的。
现在我们也知道了为啥之前的课上老师说过windows没有使用LDT表,因为LDT是任务切换用的,一个任务一个LDT表。

 

四、课后练习

 

1、使用CALL去访问一个任务段,并能够正确返回。
2、使用JMP去访问一个任务段,并能够正确返回。

 

使用CALL FAR 和JMP FAR 都可以访问任务段,有两点区别:
使用 CALL FAR 方式,EFLAGS 的 NT位置1,而JMP FAR 方式 NT位=0;
CPU根据NT位决定返回方式,如果NT=1,CPU使用TSS的 Previous task link 里存储的上一个任务的TSS选择子进行返回;如果NT=0,则使用堆栈中的值返回。

 


 

下面是两个练习题的实验步骤。这个作业卡了好久,有不少坑点,先在此说明:
坑点1:INT 3 会修改FS寄存器,所以使用 INT 3 必须先保存FS的值。
坑点2:TSS可以使用数组,也可以VirtualAlloc,建议后者,因为TSS最好是页对齐的。
坑点3:定义局部数组作为堆栈,传给TSS[14]时,应该传数组尾部的指针,因为压栈ESP减小,如果传数组首地址,那一压栈就越界了。
坑点4:JMP FAR 方式切换任务并不能提权,返回时要用先前保存的TR寄存器的值(原TSS选择子)返回。

 

补充一段Intel白皮书对于JMP 任务切换的描述:

 

(The JMP instruction cannot be used to perform inter-privilege-level
far jumps.) Executing a task switch with the JMP instruction is
somewhat similar to executing a jump through a call gate. Here the
target operand specifies the segment selector of the task gate for the
task being switched to (and the offset part of the target operand is
ignored). The task gate in turn points to the TSS for the task, which
contains the segment selectors for the task’s code and stack segments.
The TSS also contains the EIP value for the next instruc- tion that
was to be executed before the task was suspended. This instruction
pointer value is loaded into the EIP register so that the task begins
executing again at this next instruction. The JMP instruction can also
specify the segment selector of the TSS directly, which eliminates the
indirection of the task gate. See Chapter 7 in Intel® 64 and IA-32
Architectures Software Developer’s Manual, Volume 3A, for detailed
information on the mechanics of a task switch.

tr 寄存器表示当前的任务段,我们可以使用windbg 的 dg指令解析该段

可以看到当前的TR寄存器选择子是 0028

 3. TSS的作用(在Windows操作系统上)
    线程切换时,一次保存多个寄存器。
    注意:是线程切换,不是进程切换,进程切换还要替换整个环境远不止寄存器那么多!!

4. 如何查看TSS段内的内容
    TSS的地址就是TSS段描述符描述的基地址,因此我们通过 dg tr 查看其Base为 80042000。
    对于查看TSS段,有一个单独的指令。
    dt _KTSS base_address
    效果如下图(此时没用到,之后线程切换会有新的效果)

  5. 构造tss门并进行提权
  1)实验代码

// test.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "windows.h"DWORD iTSS[26];    //104个字节DWORD ESP0[0x1000];DWORD ESP3[0x1000];DWORD dwESP;DWORD dwCS;DWORD dwCR3;_declspec(naked) void Call(){_asm{mov dwESP,ESP;    //读取ESPmov ax,cs;mov dwCS,eax;    //读取csiretd;}}int main(int argc, char* argv[]){memset(iTSS,0,sizeof(iTSS));memset(ESP0,0,sizeof(ESP0));memset(ESP3,0,sizeof(ESP3));dwESP = 0;dwCS = 0;dwCR3 = 0;iTSS[1] = (DWORD)(ESP0+0x900);    // ESPiTSS[2] = 0x10;                    // SS0iTSS[8] = (DWORD)Call;            // EIPiTSS[14] = (DWORD)(ESP3+0x900); // ESP3iTSS[18] = 0x23;    // ES  iTSS[19] = 0x08;    // CS  CPL=0iTSS[20] = 0x10;    // SS  iTSS[21] = 0x23;    // DSiTSS[22] = 0x30;    // FSprintf("iTSS:%x ESP3:%x ESP0:%x\n",iTSS,(ESP3+0x900),(ESP0+0x900));printf("input cr3:");scanf("%x",&dwCR3);iTSS[7] = dwCR3;        // cr3char buf[6] = {0,0,0,0,0x48,0};_asm{call fword ptr buf;}printf("ESP:%x CS:%x\n",dwESP,dwCS);return 0;return 0;
}

  2)查看TSS的内存地址,然后来修改任务门的偏移量(注意之前为函数地址,但任务门是一次性替换全部寄存器,因此函数地址在tss的eip中)。

  0x48偏移处构造,windbg指令 eq 8003f048 0000e943`2c680068

  3)使用 !process 0 0 找到对应的CR3(物理页处),然后输入进去。

4)可以看到其直接使用了构造的esp3(虽然我们权限是零环,但它并没有使用esp0)。

 

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

 

下面是代码和执行结果,我会在代码里输出需要执行的指令。
1、使用CALL去访问一个任务段,并能够正确返回。

 

// TSS.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>DWORD dwOk;
DWORD dwESP;
DWORD dwCS;// 任务切换后的EIP
void __declspec(naked) R0Func()
{__asm{pushadpushfdpush fsint 3 // int 3 会修改FSpop fsmov eax,1mov dword ptr ds:[dwOk],eaxmov eax,espmov dword ptr ds:[dwESP],eaxmov ax,csmov word ptr ds:[dwCS],axpopfdpopadiretd}
}int _tmain(int argc, _TCHAR* argv[])
{	DWORD dwCr3; // windbg获取char esp[0x1000]; // 任务切换后的栈,数组名就是ESP// 此数组的地址就是TSS描述符中的BaseDWORD *TSS = (DWORD*)VirtualAlloc(NULL,104,MEM_COMMIT,PAGE_READWRITE);if (TSS == NULL){printf("VirtualAlloc 失败,%d\n", GetLastError());getchar();return -1;}printf("请在windbg执行: eq 8003f048 %02x00e9%02x`%04x0068\n", ((DWORD)TSS>>24) & 0x000000FF,((DWORD)TSS>>16) & 0x000000FF, (WORD)TSS);printf("请在windbg中执行!process 0 0,复制TSS.exe进程DirBase的值,并输入.\nCR3: "); // 在windbg中执行 !process 0 0 获取,DirBase: 13600420  这个数要启动程序后现查scanf("%x", &dwCr3); // 注意是%xTSS[0] = 0x00000000; // Previous Task Link CPU填充,表示上一个任务的选择子TSS[1] = 0x00000000; // ESP0TSS[2] = 0x00000000; // SS0TSS[3] = 0x00000000; // ESP1TSS[4] = 0x00000000; // SS1TSS[5] = 0x00000000; // ESP2TSS[6] = 0x00000000; // SS2TSS[7] = dwCr3; // CR3 学到页就知道是啥了TSS[8] = (DWORD)R0Func; // EIPTSS[9] = 0x00000000; // EFLAGSTSS[10] = 0x00000000; // EAXTSS[11] = 0x00000000; // ECXTSS[12] = 0x00000000; // EDXTSS[13] = 0x00000000; // EBXTSS[14] = (DWORD)esp+0x900; // ESP,解释:esp是一个0x1000的字节数组,作为裸函数的栈,这里传进去的应该是高地址,压栈才不会越界TSS[15] = 0x00000000; // EBPTSS[16] = 0x00000000; // ESITSS[17] = 0x00000000; // EDITSS[18] = 0x00000023; // ESTSS[19] = 0x00000008; // CS 0x0000001BTSS[20] = 0x00000010; // SS 0x00000023TSS[21] = 0x00000023; // DSTSS[22] = 0x00000030; // FS 0x0000003BTSS[23] = 0x00000000; // GSTSS[24] = 0x00000000; // LDT Segment SelectorTSS[25] = 0x20ac0000; // I/O Map Base Addresschar buff[6] = {0,0,0,0,0x48,0};	__asm{call fword ptr[buff]}printf("ok: %d\nESP: %x\nCS: %x\n", dwOk, dwESP, dwCS);return 0;
}

 

执行结果如图:

 

在这里插入图片描述

 

2、使用JMP去访问一个任务段,并能够正确返回。
和CALL FAR对比,NT位不会置1,TSS previous task link 也不会填充旧的TR,因此想要返回,可以先保存旧的TR,然后JMP FAR回去。

 

// TSS.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>DWORD dwOk;
DWORD dwESP;
DWORD dwCS;BYTE PrevTr[6]; // 旧TR,供裸函数返回// 任务切换后的EIP
void __declspec(naked) R3Func()
{__asm{pushadpushfdpush fsint 3 // int 3 会修改FSpop fsmov eax,1mov dword ptr ds:[dwOk],eaxmov eax,espmov dword ptr ds:[dwESP],eaxmov ax,csmov word ptr ds:[dwCS],axpopfdpopadjmp fword ptr ds:[PrevTr]}
}int _tmain(int argc, _TCHAR* argv[])
{	DWORD dwCr3; // windbg获取char esp[0x1000]; // 任务切换后的栈,数组名就是ESP// 此数组的地址就是TSS描述符中的BaseDWORD *TSS = (DWORD*)VirtualAlloc(NULL,104,MEM_COMMIT,PAGE_READWRITE);if (TSS == NULL){printf("VirtualAlloc 失败,%d\n", GetLastError());getchar();return -1;}printf("请在windbg执行: eq 8003f048 %02x00e9%02x`%04x0068\n", ((DWORD)TSS>>24) & 0x000000FF,((DWORD)TSS>>16) & 0x000000FF, (WORD)TSS);printf("请在windbg中执行!process 0 0,复制TSS.exe进程DirBase的值,并输入.\nCR3: "); // 在windbg中执行 !process 0 0 获取,DirBase: 13600420  这个数要启动程序后现查scanf("%x", &dwCr3); // 注意是%xTSS[0] = 0x00000000; // Previous Task Link CPU填充,表示上一个任务的选择子TSS[1] = 0x00000000; // ESP0TSS[2] = 0x00000000; // SS0TSS[3] = 0x00000000; // ESP1TSS[4] = 0x00000000; // SS1TSS[5] = 0x00000000; // ESP2TSS[6] = 0x00000000; // SS2TSS[7] = dwCr3; // CR3 学到页就知道是啥了TSS[8] = (DWORD)R3Func; // EIPTSS[9] = 0x00000000; // EFLAGSTSS[10] = 0x00000000; // EAXTSS[11] = 0x00000000; // ECXTSS[12] = 0x00000000; // EDXTSS[13] = 0x00000000; // EBXTSS[14] = (DWORD)esp+0x900; // ESP,解释:esp是一个0x1000的字节数组,作为裸函数的栈,这里传进去的应该是高地址,压栈才不会越界TSS[15] = 0x00000000; // EBPTSS[16] = 0x00000000; // ESITSS[17] = 0x00000000; // EDITSS[18] = 0x00000023; // ESTSS[19] = 0x00000008; // CS 0x0000001BTSS[20] = 0x00000010; // SS 0x00000023TSS[21] = 0x00000023; // DSTSS[22] = 0x00000030; // FS 0x0000003BTSS[23] = 0x00000000; // GSTSS[24] = 0x00000000; // LDT Segment SelectorTSS[25] = 0x20ac0000; // I/O Map Base Addresschar buff[6] = {0,0,0,0,0x48,0};	__asm{str axlea edi,[PrevTr+4]mov [edi],axjmp fword ptr[buff]}printf("ok: %d\nESP: %x\nCS: %x\n", dwOk, dwESP, dwCS);return 0;
}

 

在这里插入图片描述


---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/108890586
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

这篇关于[转](15)TSS,TR寄存器,TSS描述符,任务段跳转实验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/197080

相关文章

SpringQuartz定时任务核心组件JobDetail与Trigger配置

《SpringQuartz定时任务核心组件JobDetail与Trigger配置》Spring框架与Quartz调度器的集成提供了强大而灵活的定时任务解决方案,本文主要介绍了SpringQuartz定... 目录引言一、Spring Quartz基础架构1.1 核心组件概述1.2 Spring集成优势二、J

Redis实现延迟任务的三种方法详解

《Redis实现延迟任务的三种方法详解》延迟任务(DelayedTask)是指在未来的某个时间点,执行相应的任务,本文为大家整理了三种常见的实现方法,感兴趣的小伙伴可以参考一下... 目录1.前言2.Redis如何实现延迟任务3.代码实现3.1. 过期键通知事件实现3.2. 使用ZSet实现延迟任务3.3

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

如何使用Python实现一个简单的window任务管理器

《如何使用Python实现一个简单的window任务管理器》这篇文章主要为大家详细介绍了如何使用Python实现一个简单的window任务管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 任务管理器效果图完整代码import tkinter as tkfrom tkinter i

Spring Boot 集成 Quartz 使用Cron 表达式实现定时任务

《SpringBoot集成Quartz使用Cron表达式实现定时任务》本文介绍了如何在SpringBoot项目中集成Quartz并使用Cron表达式进行任务调度,通过添加Quartz依赖、创... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

Spring Boot中定时任务Cron表达式的终极指南最佳实践记录

《SpringBoot中定时任务Cron表达式的终极指南最佳实践记录》本文详细介绍了SpringBoot中定时任务的实现方法,特别是Cron表达式的使用技巧和高级用法,从基础语法到复杂场景,从快速启... 目录一、Cron表达式基础1.1 Cron表达式结构1.2 核心语法规则二、Spring Boot中定

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题小结

《SpringBoot整合ShedLock处理定时任务重复执行的问题小结》ShedLock是解决分布式系统中定时任务重复执行问题的Java库,通过在数据库中加锁,确保只有一个节点在指定时间执行... 目录前言什么是 ShedLock?ShedLock 的工作原理:定时任务重复执行China编程的问题使用 Shed

Python Invoke自动化任务库的使用

《PythonInvoke自动化任务库的使用》Invoke是一个强大的Python库,用于编写自动化脚本,本文就来介绍一下PythonInvoke自动化任务库的使用,具有一定的参考价值,感兴趣的可以... 目录什么是 Invoke?如何安装 Invoke?Invoke 基础1. 运行测试2. 构建文档3.