您的位置:首页 > 其它

关于Winnt/2k IDT的一些思考(续)

2006-03-14 22:05 656 查看
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。

[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
[ suxm: 去看看上一期月刊的图1 ]
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。

/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
#define HOOKINT 0x38
NTSTATUS DriverSpecificInitialization( )
{
PIdtEntry_t IdtEntry;
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;

NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);

/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
if (!ServiceCounterTable)
return STATUS_INSUFFICIENT_RESOURCES;

memset(ServiceCounterTable, 0, ServiceCounterTableSize);
*ServiceCounterTable=NumberOfServices;

/* 获得IDTR Register 的Base Address和Limit*/
_asm sidt buffer
IdtEntry=(PIdtEntry_t)Idtr->Base;

/* HOOKINT就是我们想要hook的中断号
这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
(IdtEntry[HOOKINT].OffsetLow);

/* 在IDT表项的相应位置写入新的中断处理函数的地址
用新的中断处理函数替换原中断处理函数 */
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
_asm sti /* Enable中断请求 */

return STATUS_SUCCESS;
}

上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
typedef struct InterruptGate
{
unsigned short OffsetLow;
unsigned short Selector;
unsigned char Reserved;
unsigned char SegmentType;
unsigned char SystemSegmentFlag;
unsigned char Dpl;
unsigned char Present;
unsigned short OffsetHigh;
} InterruptGate_t;

在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?

[ 读者小强:Winnt和2K的IDT中有空表项吗?]
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
NTSTATUS AddInterrupt()
{
PIdtEntry_t IdtEntry;

/* 获得IDTR Register 的Base Address和Limit*/
_asm sidt buffer
IdtEntry=(PIdtEntry_t)Idtr->Base;
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
return STATUS_UNSUCCESSFUL;

_asm cli

/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
IdtEntry[ADDINT].Selector=8;
IdtEntry[ADDINT].Reserved=0;
IdtEntry[ADDINT].Type=0xE;
IdtEntry[ADDINT].Always0=0;
IdtEntry[ADDINT].Dpl=3;
IdtEntry[ADDINT].Present=1;
IdtEntry[ADDINT].OffsetHigh=
(unsigned short)((unsigned int) InterruptHandler>16);
_asm sti

return STATUS_SUCCESS;
}

让我们就上面的代码提一些问题。
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
个段的名称就是8(CS=8)。

Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
应用程序的DPL=3,所以,可以直接触发这个中断。

Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
:idt
Int Type Sel:Offset Attributes Symbol/Owner
IDTbase=80036400 Limit=07FF
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
……
可见,0xE表示IntG32。

终于快到下课的时间了,让我再多说几句好吗?
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: