本文主要是介绍任务段(TSS),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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{pushadpushfdpush fs //使用int 3 会修改FS位 保存起来int 3pop fspopfdpopadiretd; //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); // ESPiTSS[2] = 0x10; // SS0iTSS[8] = (DWORD)Call; // EIPiTSS[14] = (DWORD)(ESP3+0x900); // ESP3iTSS[18] = 0x23; // ESiTSS[19] = 0x08; // CSiTSS[20] = 0x10; // SSiTSS[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);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 axstr axlea ebx,PrevTrmov [ebx+4],axpop 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); // ESPiTSS[2] = 0x10; // SS0iTSS[8] = (DWORD)Call; // EIPiTSS[14] = (DWORD)(ESP3+0x900); // ESP3iTSS[18] = 0x23; // ESiTSS[19] = 0x08; // CSiTSS[20] = 0x10; // SSiTSS[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{jmp fword ptr buf;}printf("ESP:%x CS:%x\n",dwESP,dwCS);getchar();return 0;
}
这篇关于任务段(TSS)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!