IDT:
(Interrupt Descrptor Table)中断描述符表,用来处理中断的。
IDT的获取:
- 可以通过SIDT指令,它可以在内存中找到IDT,返回一个IDTR结构的地址。
- 也可以通过kpcr结构获取
ISR:
中断服务例程
CPU通过查找在中断服务描述表(IDT)中找到的中断服务例程(ISR),知道如何响应以及对新接收到的中断执行哪些内核例程。
IDT的指针存储在每个物理处理器的IDTR寄存器中,每个处理器都有自己的IDTR寄存器,指向自己的中断描述符表。
IDT位置
可以通过读取IDTR寄存器的值来检查中断服务描述表在内核中的位置:
r idtr
IDT 结构:
实模式和保护模式
CPU复位(reset)或加电(power on)的时候以实模式启动,处理器以实模式工作。
在实模式下,内存寻址方式和8086相同,由16位段寄存器的内容乘以16(10H)当
做段基地址,加上16位偏移地址形成20位的物理地址,最大寻址空间1MB,最大分
段64KB。可以使用32位指令。32位的x86 CPU用做高速的8086。在实模式下,所
有的段都是可以读、写和可执行的。
286架构导入保护模式,允许硬件等级的存储器保护。然而要使用这些新的特色,
需要额外先前不需要的软件指令。由于x86微处理机主要的设计规格,是能够完全
地向前兼容于针对先前所有x86芯片所撰写的软件,因此286芯片的开机是处于
’实模式’—也就是关闭新的存储器保护特性的模式,所以可以运行针对旧的微处
理器所设计的软件。到现在为止,即使最新的x86 CPU一开始在电源打开处于实
模式下,也能够运行针对先前任何芯片所撰写的软件.[1]
GDT、LDT与IDT
- 在保护模式下,对一个段的描述包括3方面因素:Base Adress, Limit, Access 它们加在一起放在一个64-bit长的数据结构中,被称为段描述符。
- 段描述符使用数组存储,使用LGDT指令将GDT的入口地址装入GDTr寄存器。
- 段选择子,一个16位的数据结构。
三个都是比较重要的系统表
这三个表都是在内存中由操作系统所建。但并不是固化在那里,所以从理论上是可以被读写的。
这三个表都是描述符表。
GDT: 全局服务描述符表
LDT: 局部描述符表
IDT: 中断服务描述符表
GDT 与IDT表
GDT的理解
GDT全局服务描述表(Global Descriptor Table)
先理解一下实模式下得编程模型:
在实模式下,对一个内存地址的访问是通过Segment:Offset的方式来进行的,其中Segment就是一个段的BaseAdress,一个Segment的最大长度是64kb,这是16bit系统所能表示的最大长度。
而offset是相对于此Segment Base Adress的偏移量。 Base Adress + offset就是一个内存绝对地址。由此,我们可以看出,一个段具备两个因素:Base Address 和 Limit (段的最大长度)
对于一个内存地址访问,需要指出: 使用哪个段,及相对于这个段Base Address 的offset 这个offset应该小于此段的limit。 对于16 bit 系统 ,limit不需要指定,默认最大长度为64k,且16bit的offset永远也不会大于此limit。
在保护模式下,内存的管理模式分为两种: 段模式和页模式,其中页模式也是基于段模式的。段模式是必不可少的。页模式是可选的。
对于段模式来讲,访问一个内存地址仍然使用Segment:offset的方式。 由于保护模式运行在32位系统上,那么segment的两个因素: Base Address 和limit 都是32位的。允许将一个段的地址设为32bit所能表示的任何值,而不像实模式下,一个段的Base Address只能是16的倍数。 保护模式,顾名思义,就是又为段模式提供了保护机制。也就是说一个段的描述符需要规定对自身的访问权限(Access)。
所以,在保护模式下,对一个段的描述则包括3方面的因素:【Base Address, limit , Access】,它们加在一起被放在一个64bit长的数据结构中,被称为段描述符。
在这种情况下,如果我们直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段寄存器装入这个段描述符。但为了保持向后兼容,将段寄存器仍然规定为16-bit。(虽然
每个段寄存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段寄存器就是16bit的),那么我们无法通过16-bit的长度的段寄存器来直接引用64-bit的段描述符,怎么办?
解决的方法就是把这些长度为64bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用。这个全局的数组就是GDT。事实上,在GDT中存放的不仅仅是段描述符,还有其他描述符。
GDT可以被存放在内存的任何位置,当通过段寄存器引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里。所以Intel设计了一个寄存器GDTR用来存放GDT入口地址。
程序员将GDT设定在内存某个位置之后,可以通过:LGDT指令将GDT的入口地装入此寄存器。从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。
在整个系统中,全局服务描述表GDT只有一张(一个处理器对应一个GDT)
GDT可以被放在内存任何位置
系统用GDTR寄存器存放当前GDT表的基地址。
用LDTR寄存器存放LDT表地址
我们知道,在实模式下,CPU是16位的,意思是 寄存器是16位的,数组总线是16位的,但地址总线是20位的。物理内存计算公式:
物理地址 = 段地址 * 16 + 偏移量
段地址和偏移量都是16位,能寻址的最大内存地址为1M
2的20次方就是1M
若一个内存地址是20:30 最终的内存地址是: 20 * 16 +30
实模式和保护模式
实模式只有20位地址总线, 寻址空间只有1M ,以及8个16位的通用寄存器。4个16位的段寄存器。为了能够通过16位的寄存器去构成20位的主存地址,采取一种特殊的方法,使用下面的地址:
段基址: 段偏移量
物理地址 = 段地址 << 4 + 段内偏移
保护模式下,CPU的32条地址线全部有效,可寻址4G的字节的物理地址空间。但是为了兼容16位。此时计算机中的通用寄存器都成为32位。
在保护模式下存在一些限制。把这些限制信息放在一个全局服务描述表(GDT)中。
GDT的作用是提供段式存储机制。段寄存器提供段值,即描述符在GDT中的索引。
在进入保护模式前、必须准备好GDT、描述符和描述符选择子。
全局服务描述表:
包含一个段的基址,界限及属性内容。其中段基地址和16
位的系统中的段地址一样。只是在16位系统中段地址必须
是16的倍数,但是在32位系统中段地址可以是任意地址。
寻址方式:
段地址(选择子)------在GDT中找到描述符-------
在描述符中找到段地址-----段地址+偏移地址 = 线性地址
IDT结构体:
//idtr指向这个结构体
typedef struct _IDT_INFO{
UINT16 uIdtLimit; // IDT范围
UINT16 uLowIdtBase; // IDT低基址
UINT16 uHighIdtBase; // IDT高基址
}IDT_INFO, *PIDT_INFO;
// IDT表中描述符结构体
//0x8 bytes (sizeof)
typedef struct _IDTENTRY
{
// USHORT == UINT16
USHORT uOffsetLow; //0x0,低地址偏移
USHORT uSelector; //0x2,段选择器
//USHORT uAccess; //0x4
UINT8 uReserved; // 保留
UINT8 GateType : 4; // 中断类型
UINT8 StorageSegment : 1; // 为0则是中断门
UINT8 DPL : 2; // 特权级
UINT8 Present : 1; // 如未使用中断可置为0
USHORT uOffsetHigh; //0x6 // 高地址偏移
}IDTENTRY, *PIDTENTRY;
获取IDTR
__asm sidt stcIDT
遍历IDT:
#include <ntifs.h>
#include <ntddk.h>
#define MAKE_LONG(a,b) ((a) + (b<<16))
typedef struct _IDT_INFO {
UINT16 uIdtLimit; // IDT范围
UINT16 uLowIdtBase; // IDT低基址
UINT16 uHighIdtBase; // IDT高基址
}IDT_INFO, * PIDT_INFO;
//0x8 bytes (sizeof)
typedef struct _IDTENTRY
{
// USHORT == UINT16
USHORT uOffsetLow; //0x0,低地址偏移
USHORT uSelector; //0x2,段选择器
//USHORT uAccess; //0x4
UINT8 uReserved; // 保留
UINT8 GateType : 4; // 中断类型
UINT8 StorageSegment : 1; // 为0则是中断门
UINT8 DPL : 2; // 特权级
UINT8 Present : 1; // 如未使用中断可置为0
USHORT uOffsetHigh; //0x6 // 高地址偏移
}IDTENTRY, *PIDTENTRY;
void OnUnload(DRIVER_OBJECT* pDriver)
{
pDriver;
}
NTSTATUS DriverEntry(DRIVER_OBJECT* pDriver, UNICODE_STRING* pRegPath)
{
NTSTATUS status = STATUS_SUCCESS;
pRegPath;
pDriver->DriverUnload = OnUnload;
//KdBreakPoint();
IDT_INFO stcIDT = { 0 };
PIDTENTRY pIdtEntry = NULL;
ULONG uAddr = 0;
// IDT table
__asm sidt stcIDT;
// IDT array
pIdtEntry = (PIDTENTRY)MAKE_LONG(stcIDT.uLowIdtBase, stcIDT.uHighIdtBase);
KdPrint(("-------------IDT---------------\n"));
KdPrint(("IDT Addr: 0x%p\n", pIdtEntry));
for (ULONG i = 0; i < 0x100; ++i)
{
KdPrint(("Interrupted number: %d\n", i));
uAddr = MAKE_LONG(pIdtEntry[i].uOffsetLow, pIdtEntry[i].uOffsetHigh);
KdPrint(("Interrupted Addr: 0x%p\n", uAddr));
KdPrint(("selector: %d\n", pIdtEntry[i].uSelector));
KdPrint(("GataType: %d\n", pIdtEntry[i].GateType));
KdPrint(("DPL: %d\n\n", pIdtEntry[i].DPL));
}
return status;
}
获取IDTR
__asm sidt stcIDT
遍历IDT:
#include <ntifs.h>
#include <ntddk.h>
#define MAKE_LONG(a,b) ((a) + (b<<16))
typedef struct _IDT_INFO {
UINT16 uIdtLimit; // IDT范围
UINT16 uLowIdtBase; // IDT低基址
UINT16 uHighIdtBase; // IDT高基址
}IDT_INFO, * PIDT_INFO;
//0x8 bytes (sizeof)
typedef struct _IDTENTRY
{
// USHORT == UINT16
USHORT uOffsetLow; //0x0,低地址偏移
USHORT uSelector; //0x2,段选择器
//USHORT uAccess; //0x4
UINT8 uReserved; // 保留
UINT8 GateType : 4; // 中断类型
UINT8 StorageSegment : 1; // 为0则是中断门
UINT8 DPL : 2; // 特权级
UINT8 Present : 1; // 如未使用中断可置为0
USHORT uOffsetHigh; //0x6 // 高地址偏移
}IDTENTRY, *PIDTENTRY;
void OnUnload(DRIVER_OBJECT* pDriver)
{
pDriver;
}
NTSTATUS DriverEntry(DRIVER_OBJECT* pDriver, UNICODE_STRING* pRegPath)
{
NTSTATUS status = STATUS_SUCCESS;
pRegPath;
pDriver->DriverUnload = OnUnload;
//KdBreakPoint();
IDT_INFO stcIDT = { 0 };
PIDTENTRY pIdtEntry = NULL;
ULONG uAddr = 0;
// IDT table
__asm sidt stcIDT;
// IDT array
pIdtEntry = (PIDTENTRY)MAKE_LONG(stcIDT.uLowIdtBase, stcIDT.uHighIdtBase);
KdPrint(("-------------IDT---------------\n"));
KdPrint(("IDT Addr: 0x%p\n", pIdtEntry));
for (ULONG i = 0; i < 0x100; ++i)
{
KdPrint(("Interrupted number: %d\n", i));
uAddr = MAKE_LONG(pIdtEntry[i].uOffsetLow, pIdtEntry[i].uOffsetHigh);
KdPrint(("Interrupted Addr: 0x%p\n", uAddr));
KdPrint(("selector: %d\n", pIdtEntry[i].uSelector));
KdPrint(("GataType: %d\n", pIdtEntry[i].GateType));
KdPrint(("DPL: %d\n\n", pIdtEntry[i].DPL));
}
return status;
}
遍历GDT:
#include <ntddk.h>
#define MAKE_LONG(a,b) ((LONG)(((UINT16)(((DWORD_PTR)(a)) & 0xffff)) | ((UINT32)((UINT16)(((DWORD_PTR)(b)) & 0xffff))) << 16))
typedef struct _GDT_INFO {
UINT16 uGdtLimit;
UINT16 uLowGdtBase;
UINT16 uHighGdtBase;
}GDT_INFO, *PGDT_INFO;
//0x8 bytes (sizeof)
typedef struct _GDTENTRY
{
USHORT LimitLow; //0x0
USHORT BaseLow; //0x2
union
{
struct
{
UCHAR BaseMid; //0x4
UCHAR Flags1; //0x5
UCHAR Flags2; //0x6
UCHAR BaseHi; //0x7
} Bytes; //0x4
struct
{
ULONG BaseMid : 8;
ULONG Type : 4;
ULONG S : 1;
ULONG Dpl : 2;
ULONG Pres : 1;
ULONG LimitHi : 4;
ULONG Avl : 1;
ULONG Reserved_0 : 1;
ULONG D_B : 1;
ULONG Granularity : 1;
ULONG BaseHi : 8;
} Bits;
} HighWord;
}GDTENTRY, *PGDTENTRY;
void OnUnload(DRIVER_OBJECT* pDriver)
{
pDriver;
}
NTSTATUS DriverEntry(DRIVER_OBJECT* pDriver, UNICODE_STRING* pRegPath)
{
NTSTATUS status = STATUS_SUCCESS;
pRegPath;
pDriver->DriverUnload = OnUnload;
//KdBreakPoint();
GDT_INFO stcGDT = { 0 };
PGDTENTRY pGdtEntry = NULL;
unsigned int nGdtEntry = 0;
ULONG uData = 0;
// IDT table
__asm sgdt stcGDT;
// IDT array
pGdtEntry = (PGDTENTRY)MAKE_LONG(stcGDT.uLowGdtBase, stcGDT.uHighGdtBase);
KdPrint(("-------------GDT---------------\n"));
KdPrint(("GDT Addr: 0x%p\n", pGdtEntry));
nGdtEntry = stcGDT.uGdtLimit / 8;
for (ULONG i = 0; i < nGdtEntry; ++i)
{
if (!(pGdtEntry[i].HighWord.Bits.Pres)) continue;
uData = (ULONG)pGdtEntry[i].BaseLow
+ ((ULONG)(pGdtEntry[i].HighWord.Bits.BaseMid) << 16)
+ ((ULONG)(pGdtEntry[i].HighWord.Bits.BaseHi) << 24);
KdPrint(("BaseAddr: 0x%p\n", uData));
uData = pGdtEntry[i].LimitLow
+ ((ULONG)(pGdtEntry[i].HighWord.Bits.LimitHi) << 16);
KdPrint(("Segment Limit: 0x%08X ", uData));
(pGdtEntry[i].HighWord.Bits.Granularity)
? KdPrint(("pages\n"))
: KdPrint(("bytes\n"));
KdPrint(("DPL: %d\n", pGdtEntry[i].HighWord.Bits.Dpl));
if ((pGdtEntry[i].HighWord.Bits.S == 0))
{
KdPrint(("Type: System segment\t"));
switch (pGdtEntry[i].HighWord.Bits.Type)
{
case 12:
KdPrint(("Call Gate "));
break;
case 14:
KdPrint(("Interruptting Gate "));
break;
case 15:
KdPrint(("Trap Gate "));
break;
case 5:
KdPrint(("Task Gate "));
break;
default:
KdPrint(("Unknown "));
break;
}
KdPrint(("\n"));
}
else
{
if (pGdtEntry[i].HighWord.Bits.Type & 0x8)
{
KdPrint(("Type: Code Segment\n"));
KdPrint(("Attr: "));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x4 ? "C" : "-"));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x2 ? "R" : "-"));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x1 ? "A" : "-"));
KdPrint(("\n"));
}
else
{
KdPrint(("Type: Data Segment\n"));
KdPrint(("Attr: "));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x4 ? "E" : "-"));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x2 ? "W" : "-"));
KdPrint(("%s", pGdtEntry[i].HighWord.Bits.Type & 0x1 ? "A" : "-"));
KdPrint(("\n"));
}
}
KdPrint(("\n"));
}
return status;
}
REFERENCE
https://www.cnblogs.com/ciyze0101/p/5350613.html windows 下的IDT hook和GDT的学习
https://blog.csdn.net/ggh19/article/details/113705230?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-113705230-blog-50654629.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-113705230-blog-50654629.pc_relevant_default&utm_relevant_index=2 操作系统中的描述符和GDT