首页
论坛
专栏
课程

[原创]带你重新认识保护模式TSS任务段!

2020-3-23 16:51 1412

[原创]带你重新认识保护模式TSS任务段!

2020-3-23 16:51
1412
疫情隔离期间闲着没事又重新探索了一下i386保护模式,主要是TSS任务段,之前一直云里雾里,背后的机制不是太明白。

先说使用TSS段的一些注意事项,大神请无视:
  1. 进入TSS任务切换例程后,不能使用中断,如:int 3;
  2. TSS任务切换例程返回指令是iretd,但是若使用iret,编译器会自动加66指令前缀,操作的仍然是32位宽度的寄存器,同iretd等效,16位操作系统环境使用iret;
  3. 通过JMP FWORD指令进入任务切换例程退出时使用JMP FWORD退出到原来的TSS段,而不是使用iretd;
  4. win7操作系统在使用TSS段进入新任务后不会在旧TSS中存储CR3寄存器的值,导致执行iretd指令时出错,在新任务中加入存储CR3到旧TSS中的代码后能正常执行iretd,当然这需要0环权限,否则无法访问高2GB内存导致修正CR3失败;
  5. eflags标志寄存器的第14位NT位若为1时,表示当前CPU执行的是嵌套任务,此时CPU在执行iret/iretd指令时会将当前TR寄存器指向的TSS中取出前一个TSS段的选择子装载到TR寄存器中,并且将当前各寄存器的值改为此TSS段中各种寄存器的对应的值;若NT位为0,CPU执行iret/iretd指令时会当成普通的中断返回,也就是从当前堆栈中pop出5个DWORD(32位)到EIP,CS,EFLAGS,ESP,SS;
  6. TSS段分成好几个域,intel在设计的时候本来想通过TSS来做任务切换,但下游的操作系统厂商不听话,没有使用这个机制,原因是这样做任务切换CPU开销太大,做任务切换用到的域包括CR3,EIP,EFLAGS,8个通用寄存器,cs,ss,es,ds,fs,gs;另一个域包括ESP0,SS0,ESP1,SS1,ESP2,SS2,这个域主要是用来存储CPU提升特权级对应的堆栈;
  7. 使用TSS段做任务切换后CPU的特权级别是由TSS的CS,SS这两个选择子来决定的,加入在GDT表中加入1环的代码段和1环数据段,把这两个描述符对应的段选择子放到TSS中CS和SS的位置,当使用这个TSS做任务切换时,新任务的CPU特权级别就是1;
  8. 无论使用哪种门进入0环,FS寄存器都需要程序员自己维护,退出0环时也一样;

下图给出不同的任务切换方法对NT位,TSS描述符的B位(忙位)以及TSS任务链接域的影响:

最后附上一段在TSS任务切换不提权,切换到新任务后使用中断门提权的代码:
#include<stdio.h>
#include<Windows.h>
DWORD STACK[50] = { 0 };
DWORD TSS[]=
{
	0x00000000,//前一个TSS段选择子
	0x83f2ccb0,//esp0
	0x00000010,//ss0
	0x00000000,//esp1
	0x00000000,//ss1
	0x00000000,//esp2
	0x00000000,//ss2
	0x00000000,//CR3--TSS[7]
	0x00000000,//eip--TSS[8]
	0x00000002,//eflags
	0x11111111,//eax
	0x22222222,//ecx
	0x33333333,//edx
	0x44444444,//ebx
	(DWORD)(STACK+0x11),//esp
	0x00000000,//ebp
	0x55555555,//esi
	0x66666666,//edi
	0x00000023,//es
	0x0000001b,//cs
	0x00000023,//ss
	0x00000023,//ds
	0x0000003b,//fs
	0x00000000,//gs
	0x00000000,//ldt
	0x20ac0000,//iomap
};

void _declspec(naked) ring3()
{
	__asm
	{
		xor eax, eax;
		mov ax, fs;
		push eax;
		mov ax, cs;
		push eax;
		mov ax, ss;
		push eax;
		pushfd;
		mov eax, esp;
		push eax;
		int 34;
		mov bx, 0x1b;
		mov fs, bx;
		iretd;
	}
}

void _declspec(naked) ring0()
{
	__asm
	{
		pushfd;
		pop ecx;
		lea edi, dword ptr[STACK];
		mov dword ptr[edi], ecx;//efalgs

		mov eax, dword ptr[esp];
		mov dword ptr[edi + 0x8], eax;//eip

		mov eax, dword ptr[esp + 0x4];
		mov dword ptr[edi + 0xc], eax;//cs

		mov eax, dword ptr[esp + 0x8];
		mov dword ptr[edi + 0x10], eax;//eflags

		mov eax, dword ptr[esp + 0xc];
		mov dword ptr[edi + 0x14], eax;//esp

		mov eax, dword ptr[esp + 0x10];
		mov dword ptr[edi + 0x18], eax;//ss

		xor eax, eax;
		mov ax, cs;
		mov dword ptr[edi + 0x20], eax;
		mov ax, ss;
		mov dword ptr[edi + 0x24], eax;
		mov ax, fs;
		mov dword ptr[edi + 0x28], eax;

		mov bx, 0x30;
		mov fs, bx;
		mov eax, dword ptr fs : [0x40];
		mov edx, cr3;
		mov dword ptr[eax + 0x1c], edx;//存储CR3到原TSS中

		iretd;
	}
}
int main()
{
	memset(STACK, 0xcc, sizeof(DWORD) * 50);
	DWORD EIP = (DWORD)ring3;
	TSS[8] = EIP;
	printf("中断例程=%p\n", ring0);
	printf("请输入CR3:\n");
	scanf("%x", &TSS[7]);
	printf("TSS=%p\n请设置好TSS的段描述符以及中断门描述符!\n", (VOID*)TSS);
	system("pause");
	char buff[6];
	*((DWORD*)&buff[0]) = 0x0;
	*((WORD*)&buff[4]) = 0x0093;
	__asm
	{
		int 3;
		call fword ptr[buff];
		int 3;
		xor eax, eax;
		mov ax, 0x3b;
		mov fs, ax;
	}
	for (int i = 0; i < 18; ++i)
	{
		printf("%X\n", STACK[i]);
	}
	system("pause");
	return 0;
}






2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最后于 2020-3-23 18:15 被Mr.hack编辑 ,原因:
最新回复 (2)
yy虫子yy 2020-3-23 20:04
2
0
可以探索下x64的TSS
killpy 2 2020-3-24 10:33
3
0
x64下 如何利用TSS 硬核切换任务呢
游客
登录 | 注册 方可回帖
返回