TSS
TSS是一块内存,里面包括了任务切换需要的所有寄存器的值。
大小是104字节
TSS一个核只有一个,TSS段描述符每一个线程有一个 存在GDT表中
CPU如何找到tss这块内存
CPU里面有一个 TR段寄存器,该寄存器里面存的是TSS段描述符的段选择子。
tr的base就是tss所在的地址,tr的limit就是tss的大小 使用 ltr 特权指令加载tss段描述符
TSS段描述符
GDT表中的一个8字节的段描述符,类型是TSS
TSS结构
实验
自己实现使用TSS替换寄存器
构造TSS段描述符(位于GDT表中): XX00E9XX `XXXX0068 X为申请的104字节内存的首地址。 G位为0,单位是字节。
TSS一开始的类型是9(可用),当加载到tr中就会变成b( 正被占用)
当call 0x40:0x12345678 对应的描述符为TSS时,CPU是这样执行的
- 0x40为一个选择子,去GDT表找到对应的TSS描述符
- 加载TSS描述符到TR ,TSS描述符里面有TSS这块内存的base(地址)和limit
- 替换TSS里面的值到寄存器中
- 执行EIP
这样完成了所有寄存器值得替换,也切换了任务(线程) ,因为EIP已经改变了。
程序的返回
使用call的时候NT位会被置1,Previous Task 会被写入为上一个TSS的值 JMP不会写
NT位对iret指令 有影响:当NT=0时,iretd的返回值从堆栈里面取(中断返回)。
NT位为1时,会找TSS previous task link()
eq 8003f048 0000e940 `10100068 构造的TSS描述符 写入8003f048
//使用 jmp call 如果选择子对应的描述符是TSS ,cpu会先修改TR寄存器 然后用TR.base只想的TSS中的值修改当前的寄存器。
//
// tssss.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
DWORD iTSS[26];
DWORD ESP0[0x1000];
DWORD ESP3[0x1000];
DWORD dwESP;
DWORD dwCS;
DWORD dwCR3;
_declspec(naked) void Call(){
_asm{
pushad
pushfd
push fs //使用int 3 会修改FS位 保存起来
int 3
pop fs
popfd
popad
iretd; //cpu会加载tr寄存器对应的TSS段描述符 完成返回,也是跳转
}
}
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); // ESP
iTSS[2] = 0x10; // SS0
iTSS[8] = (DWORD)Call; // EIP
iTSS[14] = (DWORD)(ESP3+0x900); // ESP3
iTSS[18] = 0x23; // ES
iTSS[19] = 0x08; // CS
iTSS[20] = 0x10; // SS
iTSS[21] = 0x23; // DS
iTSS[22] = 0x30; // FS
printf("iTSS:%x ESP3:%x ESP0:%x\n",iTSS,(ESP3+0x900),(ESP0+0x900));
printf("input cr3:");
scanf("%x",&dwCR3);
iTSS[7] = dwCR3; // cr3
char buf[6] = {0,0,0,0,0x48,0};
_asm{
call fword ptr buf;
}
printf("ESP:%x CS:%x\n",dwESP,dwCS);
getchar();
return 0;
}
使用JMP来完成 思路:
- 保存跳转前的tr的选择子
- 使用jmp tr 跳转回来
// tssss.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
DWORD iTSS[26];
DWORD ESP0[0x1000];
DWORD ESP3[0x1000];
DWORD dwESP;
DWORD dwCS;
DWORD dwCR3;
char PrevTr[6]={0};
_declspec(naked) void Call(){
_asm{
jmp fword ptr PrevTr
}
}
int main(int argc, char* argv[])
{
__asm{
push ax
str ax
lea ebx,PrevTr
mov [ebx+4],ax
pop ax
}
printf("tr=%x\n",*(PrevTr+4));
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); // ESP
iTSS[2] = 0x10; // SS0
iTSS[8] = (DWORD)Call; // EIP
iTSS[14] = (DWORD)(ESP3+0x900); // ESP3
iTSS[18] = 0x23; // ES
iTSS[19] = 0x08; // CS
iTSS[20] = 0x10; // SS
iTSS[21] = 0x23; // DS
iTSS[22] = 0x30; // FS
printf("iTSS:%x ESP3:%x ESP0:%x\n",iTSS,(ESP3+0x900),(ESP0+0x900));
printf("input cr3:");
scanf("%x",&dwCR3);
iTSS[7] = dwCR3; // cr3
char buf[6] = {0,0,0,0,0x48,0};
_asm{
jmp fword ptr buf;
}
printf("ESP:%x CS:%x\n",dwESP,dwCS);
getchar();
return 0;
}