保护模式(六)任务段任务门

Windows保护模式学习笔记(五)—— 任务段&任务门

前言

一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com
二、海东老师牛逼!

要点回顾

  1. 在调用门、中断门与陷阱门中,一旦出现权限切换,那么就会有堆栈的切换;而且,由于CS的CPL发生改变,也导致了SS也必须要切换
  2. 思考:切换时,会有新的ESP和SS(CS是由中断门或者调用门指定)这2个值从哪里来的呢?
    答案:TSS (Task-state segment ):任务状态段

任务段

TSS (Task-state segment )

描述:

TSS是一块内存
大小:104字节
TSS存储了一堆寄存器的值

TSS结构图:
TSS结构图
TSS的作用

  1. Intel的设计思想:通过使用TSS以达到任务(线程)的切换
  2. 操作系统的设计思想:与Intel设计思想不同的是,Windows并没有根据Intel的设计思想来做,甚至Linux也没有这样做

TSS的本质

  1. 不要把TSS与"任务切换"联系到一起
  2. TSS的意义就在于可以同时换掉"一堆"寄存器

CPU通过TR段寄存器寻找TSS

TR段寄存器

描述:

TR寄存器的值是当操作系统启动时,从TSS段描述符中加载出来的,TSS段描述符在GDT表中
TR.Base = TSS起始地址
TR.Limit = TSS大小

TR段寄存器的读写

一、将TSS段描述符加载到TR寄存器
指令:LTR
说明:

  1. 用LTR指令去装载的话 仅仅是改变TR寄存器的值(96位)
  2. 并没有真正改变TSS
  3. LTR指令只能在系统层使用
  4. 加载后TSS段描述符的状态位会发生改变

二、读TR寄存器
指令:STR
说明:如果用STR去读的话,只读了TR的16位,也就是段选择子

TSS段描述符

描述:

TSS段描述符是系统段描述符中的一种

结构图:
TSS段描述符
Type = 二进制1001:说明该TSS段描述符被加载到TR段寄存器中
Type = 二进制1011:说明该TSS段描述符被加载到TR段寄存器中

TSSTSS段描述符TR段寄存器关系示意图:
TR段寄存器

实验:加载自定义TSS

第一步:获取必要参数

在VC6中运行如下代码并在main函数头部设置中断:

#include <windows.h>

DWORD dwOK;
DWORD dwESP;
DWORD dwCS;
char trs[6]={0};
void __declspec(naked) func()
{
	dwOK = 1;
	__asm
	{
		//jmp回去
		//jmp fword ptr trs;

		//call回去

		pushfd
		int 3 //此处int3会将nt位置1,不会通过link,因此提前保存eflag,返回之前还原nt位
		mov eax,esp
		mov dwESP,eax
		mov ax,cs
		mov word ptr [dwCS],ax
		popfd
		iretd

		
	}
}

int main(int argc, char* argv[])
{
	char bu[0x10];  //12ff70
	int iCr3;

	printf("input CR3:\n");
	scanf("%x", &iCr3);

	DWORD iTss[0x68] = {
		0x00000000,		//link
		0x00000000,		//esp0		//(DWORD)bu
		0x00000000,		//ss0
		0x00000000,		//esp1
		0x00000000,		//ss1
		0x00000000,		//esp2
		0x00000000,		//ss2
		(DWORD)iCr3,	//cr3
		0x0040DE50,		//eip
		0x00000000,		//eflags
		0x00000000,		//eax
		0x00000000,		//ecx
		0x00000000,		//edx
		0x00000000,		//ebx
		(DWORD)bu,		//esp
		0x00000000,		//ebp
		0x00000000,		//esi
		0x00000000,		//edi
		0x00000023,		//es
		0x00000008,		//cs		0x0000001B
		0x00000010,		//ss		0x00000023
		0x00000023,		//ds
		0x00000030,		//fs
		0x00000000,		//gs
		0x00000000,		//dit
		0x20ac0000};

		char buff[6];

		*(DWORD*)&buff[0] = 0x12345678;
		*(WORD*)&buff[4] = 0xC0;

		__asm
		{
			str ax;
			mov rs,ax;
		}
		*(WORD*)&trs[4]=rs;
		__asm
		{
			//jmp fword ptr buff;
			call fword ptr[buff]
		}

		printf("ok=%d \t ESP=%x \t CS=%x \n", dwOK, dwESP, dwCS);

		return 0;
}



进入反汇编窗口查看 func 函数起始地址,我这里是0x401020
func函数地址
将地址填入iTss数组注释为eip的地方,表示TSS切换后EIP的值
地址填入eip
再通过 memory 窗口查看iTss数组所在地址,记下来备用
iTss所在地址
注意:代码中如有地方发生修改,需要先停止程序,重新编译

第二步:构造TSS段描述符
Offset in Segment 31:16 = 0x0000		// 暂定
					  G = 0
					AVL = 0
				  Limit = 二进制:0000
				  	  P = 1
				  	DPL = 二进制:11
	   			   Type = 二进制:1001
	   	  Segment Limit = 0068H			// Intel规定TSS段描述符G=0时Limit必须大于或等于67H
Offset in Segment 15:00 = 0x0000		// 暂定

由上述参数构造出的门描述符为:0000E900`00000068

在第一步中,我们已经知道iTss数组所在地址为0x12FDCC
因此,TSS段描述符最终确定为:0000E912`FDCC0068

第三步:将TSS段描述符写入GDT表

我写入的地址是8003f0c0,若写入其他地址,则需要修改buff数组的后两个字节
在这里插入图片描述
这时候先不要急着继续运行代码,先在WinDbg中输入命令:!process 0 0
获得当前进程的Cr3(我这里进程名叫TestDoor.exe,之前是用来做调用门的实验,TSS没有新建项目)
在这里插入图片描述
DirBase的值就是Cr3

第四步:解除中断,继续执行代码

输入上一步得到的Cr3,回车
输入iCr3
WinDbg成功获得了中断信号
int3
这时候看一下反汇编代码
反汇编
可以确定正在执行func函数的代码

再看一下寄存器
reg
eip、cs、ss都符合我们想要的结果

至此,TSS切换成功!


思考:TSS切换完成后,如何回到切换前的下一行继续执行?

任务门

描述:

任务门存在于IDT表
任务门中包含TSS段选择子
可以通过访问任务门达到切换TSS的目的

结构图:
任务门

任务门执行过程

  1. INT N(N为IDT表索引号)
  2. 系统通过用户指定的索引查找IDT表,找到对应的门描述符
  3. 门描述符若为任务门描述符,则根据任务门描述符中TSS段选择子查找GDT表,找到TSS段描述符
  4. 将TSS段描述符中的内容加载到TR段寄存器
  5. TR段寄存器通过Base和Limit找到TSS
  6. 使用TSS中的值修改寄存器
  7. IRETD返回

实验:通过任务门切换TSS

第一步:构造任务门描述符

任务门描述符结构图灰色部分默认填充为0

					  P = 1
					DPL = 二进制:11
   TSS Segment Selector = 0x00C3		// TSS段描述符选择子

由上述参数及默认参数构造出的门描述符为:0000e500`00C30000

第二步:将任务门描述符写入IDT表

任务门写入IDT表

第三步:构造TSS段描述符

TSS段描述符构造具体过程参照任务段实验部分,这里不再详解,只给出测试代码

代码如下:

#include <windows.h>

DWORD dwOK;
DWORD dwESP;
DWORD dwCS;

void __declspec(naked) func()
{
	dwOK = 1;
	__asm
	{
		mov eax,esp
		mov dwESP,eax
		mov ax,cs
		mov word ptr [dwCS],ax
		iretd
	}
}

int main(int argc, char* argv[])
{
	char bu[0x10];  //12ff70
	int iCr3;

	printf("input CR3:\n");
	scanf("%x", &iCr3);

	DWORD iTss[0x68] = {
		0x00000000,		//link
		0x00000000,		//esp0		//(DWORD)bu
		0x00000000,		//ss0
		0x00000000,		//esp1
		0x00000000,		//ss1
		0x00000000,		//esp2
		0x00000000,		//ss2
		(DWORD)iCr3,	//cr3
		0x00401020,		//eip
		0x00000000,		//eflags
		0x00000000,		//eax
		0x00000000,		//ecx
		0x00000000,		//edx
		0x00000000,		//ebx
		(DWORD)bu,		//esp
		0x00000000,		//ebp
		0x00000000,		//esi
		0x00000000,		//edi
		0x00000023,		//es
		0x00000008,		//cs		0x0000001B
		0x00000010,		//ss		0x00000023
		0x00000023,		//ds
		0x00000030,		//fs
		0x00000000,		//gs
		0x00000000,		//dit
		0x20ac0000};

		__asm
		{
			int 0x20
		}

		printf("ok=%d \t ESP=%x \t CS=%x \n", dwOK, dwESP, dwCS);
		getchar();
		return 0;
}

第四步:运行代码

需要输入Cr3,Cr3获取流程参照任务段实验部分,这里不再详解

运行结果:
任务门运行结果
TSS切换成功!

至此,我们已经学会通过CALL/JMP以及任务门来切换TSS


思考:既然已经可以直接访问任务段了,为什么还要有任务门?

答:任务段位主动调用,任务门位被动调用,例如int 8 练习:请进入一环,流畅运行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值