《Undocumented Windows 2000 Secrets》翻译 --- 第四章(5)
第四章 探索Windows 2000的内存管理机制
翻译:Kendiv( fcczj@263.net )
更新:
声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。
IOCTL函数SPY_IO_INTERRUPT
SPY_IO_INTERRUP类似于SPY_IO_SEGEMT,不过该函数仅影响存储在系统中断描述符表(IDT)的中断描述符,不会涉及LDT或GDT描述符。IDT最多可容纳256个描述符,这些描述符可用来描述任务门、中断门或陷阱门(参见Intel 1999c, pp. 5-11ff)。顺便说一下,中断和陷阱在本质上十分相似,二者只存在微小的差异:在进入一个中断处理例程后,总是会屏蔽其他中断;而进入陷阱处理例程却不会修改中断标志。SPY_IO_INTERRUPT的调用者提供一个0到255之间的中断号,该中断号将位于输入缓冲区中,而一个SPY_INTERRUPT结构将作为输出数据被存放到输出缓冲区中,如果成功返回,该结构中将包含对应的中断处理例程的属性。由Dispatcher调用的帮助函数SpyOutputInterrupt()只是一个简单的外包函数,它实际上调用SpyInterrupt()函数并且将需要返回的数据复制到输出缓冲区中。列表4-18给出了这两个函数,以及它们操作的SPY_INTERRUPT结构。稍后一些,SpyInterrupt()函数将填充如下项目:
l Selector 用来指定一个任务状态段(Task-State Segment, TSS)或代码段(Code Segment)的选择器。代码段选择器用来确定中断或陷阱处理例程所在的段。
l Gate 用来表示一个64位的任务门、中断门或陷阱门描述符,由Selector确定其地址。
l Segment 包含段的属性,该段的地址由前面的Gate给出。
l pOffset 指定中断或陷阱处理例程的入口地址相对基地址的偏移量。这里的基地址是指中断或陷阱处理例程所在代码段的起始地址。因为任务门不包含偏移量,所以,如果输入的选择器指向一个TSS,则忽略该成员。
l fOk 一个标志变量,用来指示SPY_INTERRUPT结构中的数据是否有效。
通常情况下,TSS被用来保证一个错误情况可以被一个有效的任务处理。这是一个特殊的系统段类型(system segment type),它可以保存104个字节的进程状态信息,该信息在任务切换时,用来进行任务的恢复,如表4-3所示。当与任务相关的中断发生时,CPU总是强制切换该任务,并将所有的CPU寄存器保存到TSS中。Windows 2000在中断位置0x02(非屏蔽中断[NMI],0x08[Double Fault]和0x12[堆栈段故障])处保存任务门。剩余的位置指向中断处理例程。不使用的中断由一个哑元例程---KiUnexpectedInterruptNNN()处理,这里的NNN为一个十进制数。这些哑元例程最后都汇集到内部函数KiEndUnexpectedRange(),在这里,这些例程将依次进入KiUnexpectedInterruptTail()。
typedef struct _SPY_INTERRUPT
{
X86_SELECTOR Selector;
X86_GATE Gate;
SPY_SEGMENT Segment;
PVOID pOffset;
BOOL fOk;
}
SPY_INTERRUPT, *PSPY_INTERRUPT, **PPSPY_INTERRUPT;
#define SPY_INTERRUPT_ sizeof (SPY_INTERRUPT)
// -----------------------------------------------------------------
NTSTATUS SpyOutputInterrupt (DWORD dInterrupt,
PVOID pOutput,
DWORD dOutput,
PDWORD pdInfo)
{
SPY_INTERRUPT si;
SpyInterrupt (dInterrupt, &si);
return SpyOutputBinary (&si, SPY_INTERRUPT_,
pOutput, dOutput, pdInfo);
}
// -----------------------------------------------------------------
BOOL SpyInterrupt (DWORD dInterrupt,
PSPY_INTERRUPT pInterrupt)
{
BOOL fOk = FALSE;
if (pInterrupt != NULL)
{
if (dInterrupt <= X86_SELECTOR_LIMIT)
{
fOk = TRUE;
if (!SpySelector (X86_SEGMENT_OTHER,
dInterrupt << X86_SELECTOR_SHIFT,
&pInterrupt->Selector))
{
fOk = FALSE;
}
if (!SpyIdtGate (&pInterrupt->Selector,
&pInterrupt->Gate))
{
fOk = FALSE;
}
if (!SpySegment (X86_SEGMENT_OTHER,
pInterrupt->Gate.Selector,
&pInterrupt->Segment))
{
fOk = FALSE;
}
pInterrupt->pOffset = SpyGateOffset (&pInterrupt->Gate);
}
else
{
RtlZeroMemory (pInterrupt, SPY_INTERRUPT_);
}
pInterrupt->fOk = fOk;
}
return fOk;
}
// -----------------------------------------------------------------
PVOID SpyGateOffset (PX86_GATE pGate)
{
return (PVOID) (pGate->Offset1 | (pGate->Offset2 << 16));
}
列表4-18. 查询中断属性
表4-3. 任务状态段(TSS)中的CPU状态域
|
偏移量 |
位数 |
ID |
描 述 |
|
0x00 |
16 |
|
前一个任务的链接 |
|
0x04 |
32 |
ESP0 |
Ring0级的堆栈指针寄存器 |
|
0x08 |
16 |
SS0 |
Ring0级的堆栈段寄存器 |
|
0x0C |
32 |
ESP1 |
Ring1级的堆栈指针寄存器 |
|
0x10 |
16 |
SS1 |
Ring1级的堆栈段寄存器 |
|
0x14 |
32 |
ESP2 |
Ring2级的堆栈指针寄存器 |
|
0x18 |
16 |
SS2 |
Ring2级的堆栈段寄存器 |
|
0x1C |
32 |
CR3 |
页目录基址寄存器(PDBR) |
|
0x20 |
32 |
EIP |
指令指针寄存器 |
|
0x24 |
32 |
EFLAGS |
处理器标志寄存器 |
|
0x28 |
32 |
EAX |
通用寄存器 |
|
0x2C |
32 |
ECX |
通用寄存器 |
|
0x30 |
32 |
EDX |
通用寄存器 |
|
0x34 |
32 |
EBX |
通用寄存器 |
|
0x38 |
32 |
ESP |
堆栈指针寄存器 |
|
0x3C |
32 |
EBP |
基地址指针寄存器 |
|
0x40 |
32 |
ESI |
源索引寄存器 |
|
0x44 |
32 |
EDI |
目标索引寄存器 |
|
0x48 |
16 |
ES |
扩展段寄存器 |
|
0x4C |
16 |
CS |
代码段寄存器 |
|
0x50 |
16 |
SS |
堆栈段寄存器 |
|
0x54 |
16 |
DS |
数据段寄存器 |
|
0x58 |
16 |
FS |
附加的数据段寄存器#1 |
|
0x5C |
16 |
GS |
附加的数据段寄存器#2 |
|
0x60 |
16 |
LDT |
本地描述符标的段选择器 |
|
0x64 |
1 |
1 |
调试陷阱标志 |
|
0x66 |
16 |
|
I/O Map的基地址 |
|
0x68 |
- |
|
CPU状态信息结束 |
SpyInterrupt()调用的SpySegment()、SpySelector()函数已经在列表4-5和列表4-16中给出。SpyGateOffset()位于列表4-18的末尾,它的工作和SpyDescriptorBase()、SpyDescriptorLimit()类似,从X86_GATE结构中取出Offset1和Offset2位域,并适当的组织它们以构成一个32位地址。SpyIdtGaet()定义于列表4-19。它与SpyDescriptor()十分类似。汇编指令SIDT存储一个48位的值,该值就是CPU的IDT寄存器的内容,它由一个16位的表大小限制值和IDT的32位线性基地址构成。列表4-19中的剩余代码将选择器的描述符索引和IDT的大小限制值进行比较,如果OK,则对应的中断描述符将被复制到调用者提供的X86_GATE结构中。否则,门结构的所有成员都将被设置为0。
BOOL SpyIdtGate (PX86_SELECTOR pSelector,
PX86_GATE pGate)
{
X86_TABLE idt;
PX86_GATE pGates = NULL;
BOOL fOk = FALSE;
if (pGate != NULL)
{
if (pSelector != NULL)
{
__asm
{
