这两天读了combojiang(此君真乃天牛也)一篇关于IDT Hook的文章,于是自己实现了一个Hook鼠标中断服务的程序,自己也总结一下。
IDT即Interrupt Descriptor Table,描述了系统硬件/软件中断以及异常,这张表总共描述了0x7ff个中断(XP SP2),表中的每个元素代表一个中断门(Interrupt Gate),其格式如下所示:
在我的程序中将该结构定义为:
typedef struct _IDTENTRY
{
unsigned short lw_offset;
unsigned short seg_selector;
unsigned char dword_count:5;
unsigned char unused0:3;
unsigned char type:4;
unsigned char unused1:1;
unsigned char dpl:2;
unsigned char present:1;
unsigned short hi_offset;
}IDTENTRY,*PIDTENTRY;
IDT Hook的核心思想就是找到你所需要的ISR(Interrupt Service Routine)的中断门,并修改其地址,即上述结构中的lw_offset与hi_offset。
因此问题的关键就是定位中断门位置,也就是找出IDT的首地址以及中断号。中断号可以使用内核调试器(我用WinDbg)很容易的找到。386中IDT的首地址保存在寄存器IDTR中,这是一个48bits的寄存器,其结构如下所示:
该寄存器可以使用sidt加载,为方便起见,我把IDTR定义为下面的结构:
typedef struct _IDTR
{
unsigned short limit;
unsigned short lwaddr;
unsigned short hiaddr;
}IDTR,*PIDTR;
这样我们就基本解决了IDT Hook的难点了,具体代码如下:
#include <ntddk.h>
#include "idthook.h"
static unsigned int gsCount = 0;
static unsigned long gs_old_inter;
unsigned long __stdcall SetIDTEntry(unsigned char offset,unsigned long addr)
{
IDTR idtr;
PIDTENTRY pIDTEntry;
unsigned long old_inter;
__asm
{
sidt idtr
}
DbgPrint("Address:%x%x/tLimit:%x",idtr.hiaddr,idtr.lwaddr,idtr.limit);
pIDTEntry = (PIDTENTRY)((unsigned long)(idtr.hiaddr)<<16 | idtr.lwaddr );
#ifndef NDEBUG
for(i=0;i<0x7ff;i++)
{
DbgPrint("IDTNO:%x/tISR:%x%x/tPresent:%x/tDPL:%x/tTYPE:%x/tSelector:%x/tDWORD_Count:%x/tUnused0:%x/tUnused1:%x",
i,
pIDTEntry[i].hi_offset,
pIDTEntry[i].lw_offset,
pIDTEntry[i].present,
pIDTEntry[i].dpl,
pIDTEntry[i].type,
pIDTEntry[i].seg_selector,
pIDTEntry[i].dword_count,
pIDTEntry[i].unused0,
pIDTEntry[i].unused1
);
}
#endif
old_inter = (unsigned long)((unsigned long)pIDTEntry[offset].hi_offset << 16 | pIDTEntry[offset].lw_offset );
__asm
{
cli
}
pIDTEntry[offset].hi_offset = (unsigned short)(addr >> 16);
pIDTEntry[offset].lw_offset = (unsigned short)addr;
__asm
{
sti
}
return old_inter;
}
NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("Driver Unloading...");
SetIDTEntry(0xa3,gs_old_inter);
DbgPrint("Key Board Interrupt Count:%x",gsCount);
return STATUS_SUCCESS;
}
void __stdcall count_interrupt()
{
gsCount++;
}
__declspec(naked) inter_hook()
{
__asm
{
call count_interrupt
jmp gs_old_inter
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING path)
{
DriverObject->DriverUnload = DriverUnload;
DbgPrint("Driver Loading...");
gs_old_inter = SetIDTEntry(0xa3,(unsigned long)inter_hook);
return STATUS_SUCCESS;
}