要点回顾
前文中讲解了如何通过CALL、JMP指令访问任务段。
本文主要介绍如何通过任务门访问任务段。
既然已经可以通过JMP、CALL访问任务段了,那么为什么还要有任务门呢?
IDT(中断描述符表)
IDT表可以包含3种门描述符:
- 任务门描述符。
- 中断门描述符。
- 陷阱门描述符。
任务门描述符
注意:
TYPE位现在应该是5(任务门)。前文提过,当为9或B的时候代表什么。
低4字节16-31位存储的是一个TSS段的描述符选择子。既不是地址也不是偏移,而是TSS段的描述符。
任务门麻烦在于跨表,任务门本身在IDT表中,而低4字节16-31位又指定了一个TSS段描述符,TSS段描述符在GDT表中。
任务门执行过程
INT N-->查IDT表,找到中断门描述符-->通过中断门描述符查询GDT表,找到任务段描述符-->使用TSS段中的值修改寄存器-->IRETD返回
需要注意:INT指令,后面的N表示索引。
实验注意事项
至少构造两个描述符:
- 任务门描述符:构造出来以后写入IDT表中。
- TSS段描述符:构造出来以后写入GDT表中。
代码
#include "stdafx.h"
#include <windows.h>
DWORD dwOK;
DWORD dwESP;
DWORD dwCS;
__declspec(naked)func() //函数地址:0x00401020
{
dwOK = 1;
__asm
{
mov eax,esp
mov dwESP,eax
mov ax,cs
mov word ptr [dwCS],ax
iretd
}
}
//eq 8003f500 0000e500`00c30000 IDT表任务门描述符
//eq 8003f0c0 0000e912`fdcc0068 TSS任务段描述符
int main(int argc, char* argv[])
{
char bu[0x10]; //0x12ff70
int iCr3;
printf("input CR3:\n");
scanf("%x",&iCr3); //通过windbg工具指令:!process 0 0 获取
DWORD iTss[0x68] =
{
0x00000000, //link 当一次性切换一堆寄存器的时候,该地方存储的就是原104个字节的段选择子,该值不需要填充,当发生切换时由CPU自动完成
//因为并不是中断门或调用门,故esp0、ss0,esp1、ss1,esp2、ss2可以填0
0x00000000, //esp0 (DWORD)bu 想进0环取esp0和ss0
0x00000000, //ss0
0x00000000, //esp1 想进1环取esp1和ss1 Windows并未使用1环,自己使用时可以相进就近
0x00000000, //ss1
0x00000000, //esp2 想进2环取esp2和ss2 Windows并未使用2环,自己使用时可以相进就近
0x00000000, //ss2
(DWORD)iCr3, //cr3 目前只需要知道它是个寄存器即可, 必须填写
0x00401020, //eip 下一次执行的代码位置, 必须填写
0x00000000, //eflags
0x00000000, //eax
0x00000000, //ecx
0x00000000, //edx
0x00000000, //ebx
(DWORD)bu, //esp 代码执行切换的时候, 一旦发生切换所有寄存器都会发生变化, EIP变了那么ESP也会发生变化, 上面定义的数组当成堆栈来用
0x00000000, //ebp
0x00000000, //esi
0x00000000, //edi
0x00000023, //es 无论3环还是0环都是23
0x00000008, //cs 0x0000001B 3环1B, 0环08
0x00000010, //ss 0x00000023 3环23, 0环10
0x00000023, //ds 无论3环还是0环都是23
0x00000030, //fs 0x0000003B 3环3B, 0环30
0x00000000, //gs 永远是0, window并未使用
0x00000000, //ldt
0x20ac0000 //IO权限位图, Windows2000以后并未使用, 但结构中仍然存在
};
__asm
{
int 0x20
}
printf("ok = %d, ESP = %x, CS = %x\n", dwOK, dwESP, dwCS);
return 0;
}
实验
1.找到CR3并输入至程序中
可以看到代码中断了。
思考
- 既然已经可以通过JMP、CALL访问任务段了,那么为什么还要有任务门呢?
- CPU提供TSS它的目的是希望我们在任务切换的时候,通过硬件级别的方式来切换,但是操作系统并没有使用,无论是Windows还是Linux都没有使用硬件提供的这种方式来进行线程切换,那么它什么时候使用了呢?