解析Windows2000的IDT扩展机制
2006-07-20 11:40:19 来源:WEB开发网作为普通的Windows程序员,或许您需要的是熟悉对系统基本功能的操作,以及对通用程序开发的熟练掌握。但对于一个有想法的Windows内核级分析开发人员来说,对系统底层的深入了解是非常必要的,同时也是非常重要的。Hook为我们创造了一个绝好的机会,它使我们了解系统内部运行机制的想法成为了一种可能。同时,书写一个系统相关的监视程序可以自动的对系统内部操作进行记录与分析。当然我们不能局限于对系统的了解,我们更渴望实施对系统的修改与扩展,改变系统原有的操作特性,注入我们需要的功能组件,让系统做更适合我们自己,也是我们最希望看到的操作。前面我们曾经谈到了创建系统服务调用的钩子来截获系统服务调用,同样在Windows2000下,系统服务是通过系统服务中断(System Service Interrupt,int 0x2e)来实现的,通过截获软件中断同样可以达到监视并修改系统服务调用的功能。在此我们主要讨论的是为软件中断创建钩子,不过对于硬件中断和异常也同样不例外,我们同样可以将本文提到的方法应用于硬件中断和异常。比如我们也可以通过截获键盘驱动的中断调用来书写内核级的键盘记录器,它可以直接对每次击键和释放进行操作,效果是非常的明显,不过这还需要使用到一些微软为我们提供的与硬件中断钩子相关的函数。
5、如何创建软件中断钩子?
其实创建软件中断钩子的过程应该是比较明显了,下面我们将先简要介绍一下创建Hook的过程,然后以实际代码进行具体的讲解。首先我们通过汇编指令sidt(sidt: Store Interrupt Descriptor Table Register;lidt: Load Interrupt Descriptor Table Register)来获取IDT的基地址IDTBase,然后我们在中断描述符表中搜寻我们需要HOOK的中断号HOOKINTID,它应该是在0-255内的一个整数,虽然最新的Intel处理器声称支持8192个中断描述符单元,但由于某些限制原因,仍然只能处理前256个中断描述门。在找到我们需要Hook的中断描述门后,将它原本的中断执行代码偏移量(32位)保存到一个全局变量OldISR中,以备我们在执行中断处理或恢复IDT时使用。这样新的IDT中对应中断号的执行代码偏移量就指向了我们自己的处理代码了。在我们的处理代码NewISR中,注意先要保存一些线程环境,在处理完我们额外添加的执行程序(Monitor,监视注册表相关的16个系统服务调用)后,恢复现场并执行中断门以前指向的程序代码。这样,对外就看不出我们对中断门做了什么额外的处理,感觉和以前没什么两样!如果我们只是处理了我们添加的代码而没有继续执行中断门对应的以前的程序代码,那么系统必将混乱甚至崩溃!同样在我们卸载我们的软件中断钩子时,就是进行了一个逆向工作。先获取IDT的基地址,然后将保存在全局变量中的旧的执行代码地址偏移量赋给对应中断号的偏移量单元(OffsetLow/OffsetHigh)。大概过程讲得差不多了,相关程序为T-HookInt,我们再看看代码吧! VOID
6、添加软件中断的作用与原理
HookInt(VOID)
{
//保存IDT入口的基地址和限制信息的数据结构;
IDTR idtr;
//记录IDT数组的指针,通过它可以查找到我们需要Hook中断号对应的中断门;
PIDTENTRY IdtEntry;
//汇编指令sidt,获取IDT入口信息;
__asm sidt idtr;
//赋予IDT基地址值;
IdtEntry = (PIDTENTRY)idtr.IDTBase;
//保存中断号HOOKINTID对应中断门所指向的执行代码偏移量,以备执行中断处理或恢复时使用;
OldISR = ((unsigned int)IdtEntry[HOOKINTID].OffsetHigh << 16) │ (IdtEntry[HOOKINTID].OffsetLow);
//关中断
__asm cli
//更新执行代码偏移量的底16位;
IdtEntry[HOOKINTID].OffsetLow = (unsigned short)NewISR;
//更新执行代码偏移量的高16位;
IdtEntry[HOOKINTID].OffsetHigh = (unsigned short)((unsigned int)NewISR >> 16);
//开中断
__asm sti;
}
VOID
UnhookInt(VOID)
{
IDTR idtr;
PIDTENTRY IdtEntry;
__asm sidt idtr;
IdtEntry = (PIDTENTRY)idtr.IDTBase;
__asm cli
//恢复中断号HOOKINTID对应中断门执行代码偏移量的底16位;
IdtEntry[HOOKINTID].OffsetLow = (unsigned short)OldISR;
//恢复中断号HOOKINTID对应中断门执行代码偏移量的高16位;
IdtEntry[HOOKINTID].OffsetHigh = (unsigned short)((unsigned int)OldISR >> 16);
__asm sti;
}
VOID
__fastcall
Monitor()
{
……
//由于我们处理的中断号为0x2e,
//对应于系统服务中断(System Service Interrupt),
//通过获取eax寄存器中的数值来区分系统服务调用;
__asm mov dwServiceId,eax;
//执行内核函数获取当前进程的ID号;
dwProcessId = (unsigned int)PsGetCurrentProcessId();
//提升当前IRQL,防止被中断;
KeRaiseIrql(HIGH_LEVEL,&OldIrql);
switch(dwServiceId)
{
//如果eax对应的数值为0x23,
//则对应于Windows2000的ZwCreateKey系统服务调用;
case 0x23:
DbgPrint("ProcessId: %d ZwCreateKey\n",dwProcessId);
break;
……
default:
break;
}
//恢复原始IRQL;
KeLowerIrql(OldIrql);
}
更多精彩
赞助商链接