|
虽然图中都是英文,我想也不难看懂是吧。如果我们把它看作一个64位无符号整数,那么最低16位和最高16位合起来成为一个32位的位移。由于段寄存器是按线性地址设置的,这32位的位移实际上就是个32位地址,即程序入口。
数据结构KIDTENTRY的定义反映了CPU对中断描述项的格式要求(./include/ndk/i386/ketypes.h):
//
// IDT Entry 定义
//
typedef struct _KIDTENTRY
{
USHORT Offset;
USHORT Selector;
USHORT Access;
USHORT ExtendedOffset;
} KIDTENTRY, *PKIDTENTRY;
而中断向量表则应该是KIDTENTRY结构数组(./include/ntoskrnl/include/internal/i386/ke.h):
extern KIDTENTRY KiIdt[MAXIMUM_IDTVECTOR+1];
KiIdt就是前面见到过的汇编代码中的_KiIdt。由于前面的内容不符合CPU的格式要求,所以需要进行调整。这个工作在ReactOS里面是由KeInitExceptions()完成的(./include/ntoskrnl/ke/i386/exp.c)。
VOID
INIT_FUNCTION
NTAPI
KeInitExceptions(VOID)
{
ULONG i;
USHORT FlippedSelector;
/* 循环IDT */
for (i = 0; i <= MAXIMUM_IDTVECTOR; i++)
{
/* 保存当前选择码 */
FlippedSelector = KiIdt[i].Selector;
/* Flip Selector和Extended Offset */
KiIdt[i].Selector = KiIdt[i].ExtendedOffset;
KiIdt[i].ExtendedOffset = FlippedSelector;
}
}
这样处理之后,KiIdt[]的内容就符合CPU的格式要求了。
再看IDT表(见第3篇日志)中两个常数INT_32_DPL0和KGDT_R0_CODE,它们的值分别为0x8E00和0x8。
宏INT_32_DPL的定义在./ntoskrnl/include/internal/i386/asmmacro.S中。0x8E对应的就是10001110,对照上面图中的位置,我们可以读出(1)(00)0(1)110中的P、DPL、和D值(在前面加了括号的位就是)。含义在图的右下角有,这里翻译过来就是有效描述项-系统空间-32位。
宏KGDT_R0_CODE定义在./include/ndk/i386/ketypes.h中。0x8即装入段寄存器CS中的段选(学过嵌入式的同学都很熟悉这个词的,对吧),这里选择的是“全局段描述表”(Global Descriptor Table, GDT)中的表项。我们知道,一个16位的段选可以划分为3部分:Index、TI和RPL,如图。
而[0x8] = [0000000000001000] = [0000000000001][00][0] = [GDT Index][GDT/LDT][RPL],于是我们选择的是GDT[1]。具体的大概就是如下的内容,我们暂且不管下面是什么啦~
GDT[1] = {.base=0, .limit=0xffffffff, .type=0x9A};
windows提供的自陷指令
int 3:程序入口为_KiTrap3()。用于debug时设置断点。
int 0x2e:程序入口为_KiSystemService()。用于实现windows系统调用。
int 0x2a:程序入口为_KiGetTickCount()。让用户空间程序获取高精度时钟计数。
int 0x2b:程序入口为_KiCallbackReturn()。特殊自陷入口。用于在内核中回调用户空间中的子程序,执行完后,通过0x2b返回系统空间。
int 0x2c/int 0x2d:用于debug。
小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)
GMT+8, 2024-3-29 06:56
Powered by Discuz! X3.4
© 2001-2023 Discuz! Team.