Hook 所有IDT 单核

IDT,Interrupt Descriptor Table,中断描述符表中详述了IDT的概念以及三种不同的描述符,现在用代码来实现Hook 所有IDT,代码出自combojiang大牛的文章http://bbs.pediy.com/showthread.php?t=59867

思路是这样的:

通过修改ISR的入口地址跳转到自己写的汇编代码,然后保存当前堆栈环境,再跳转到自己的处理函数,进行完自己的处理后(下面的处理只是简单的记录中断被调用的次数),再跳转回原始ISR入口地址。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <ntddk.h>
#include <stdio.h>

#define START_IDT_OFFSET 0x00
#define  MAX_IDT_ENTRIES  0xff
#define MAKELONG(a, b) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16))
unsigned long g_i_count[MAX_IDT_ENTRIES];
unsigned long old_ISR_pointer[MAX_IDT_ENTRIES];

#pragma pack(1)
typedef struct
{
  unsigned short IDTLimit;     //IDT范围  占8位
  unsigned short LowIDTbase;   //基地址低8位
  unsigned short HiIDTbase;    //基地址高8位
} IDTINFO;

typedef struct
{
  unsigned short LowOffset;    //中断处理函数地址低16位
  unsigned short selector;
  unsigned char unused_lo;
  unsigned char segment_type:4;   //0x0E is an interrupt gate
  unsigned char system_segment_flag:1;
  unsigned char DPL:2;          // descriptor privilege level
  unsigned char P:1;             /* present */
  unsigned short HiOffset;    //中断处理函数地址高16位
} IDTENTRY;
#pragma pack()

#ifdef _DEBUG
char jump_template[] = {
  0x90,                    //nop, debug
  0x60,                    //pushad
  0x9C,                    //pushfd
  0xB8, 0xAA, 0x00, 0x00, 0x00,            //mov eax, AAh
  0x90,                    //push eax
  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,          //call 08:44332211h
  0x90,                    //pop eax
  0x9D,                    //popfd
  0x61,                    //popad
  0xEA, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00          //jmp 08:44332211h
};
#else
char jump_template[] = {
  0x90,                    //nop, debug
  0x60,                    //pushad
  0x9C,                    //pushfd
  0xB8, 0xAA, 0x00, 0x00, 0x00,            //mov eax, AAh
  0x50,                    //push eax
  0x9A, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00,        //call 08:44332211h
  0x58,                    //pop eax
  0x9D,                    //popfd
  0x61,                    //popad
  0xEA, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00          //jmp 08:44332211h
};
#endif

void __stdcall count_interruptes(unsigned long inumber)
{
    //ebp+0x4h
    unsigned long* aCountP;   //sub esp ,4
    unsigned long aNumber;    //sub esp ,4
  // due to far call, we need to correct the base pointer
  // the far call pushes a double dword as the return address
  // and I don't know how to make the compiler understand this
  // is a __far __stdcall (or whatever it's called)
  // anyway:
  //
  // [ebp+0Ch] == arg1
  //
    __asm{
        mov eax,[ebp+0Ch]
        mov aNumber,eax
    }

    aNumber = aNumber & 0x000000FF;   //不挂钩0xFF
    aCountP = &g_i_count[aNumber];
    InterlockedIncrement(aCountP);
}

char* idt_detour_tablebase;

void HookInterrupt()
{
    unsigned long count;
    IDTINFO idt_info;
    IDTENTRY* idt_entries;
    IDTENTRY* i;
    unsigned long addr;
    char _t[255];

    for(count = START_IDT_OFFSET; count <= MAX_IDT_ENTRIES; count++)
    {
        g_i_count[count] = 0;
    }
   
    //加载idt_info
    __asm sidt idt_info;
    idt_entries = (IDTENTRY*)MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);
    //保存旧的IDT指针
    for(count = START_IDT_OFFSET; count <= MAX_IDT_ENTRIES; count++)
    {
        i = &idt_entries[count];
        addr = MAKELONG(i->LowOffset,i->HiOffset);
        _snprintf(_t,253,"interrupt id: %d   ISR:0x%08x \n",count,addr);
        DbgPrint(_t);
        old_ISR_pointer[count] = MAKELONG(idt_entries[count].LowOffset,idt_entries[count].HiOffset);
    }
    //设定欺骗表,分配足够多的内存给跳转模板,这块内存当然位于非分页中
    idt_detour_tablebase = (char*)ExAllocatePool(NonPagedPool,sizeof(jump_template)*256);
    for(count = START_IDT_OFFSET; count <= MAX_IDT_ENTRIES; count++)
    {
        //总跳模板中偏移 = 模板单位大小*序号
        int offset = sizeof(jump_template)*count;
        //入口处 = 总模板起始地址 + 偏移
        char* entry_ptr = idt_detour_tablebase + offset;
        //拷贝计算出来的模板入口
        memcpy(entry_ptr, jump_template,sizeof(jump_template));
#ifndef _DEBUG
        //发布模式
        //把模板中0xAA改为序号
        entry_ptr[4] = (char)count;
        //来统计中断被调用了多少回
        *((unsigned long*)(&entry_ptr[10])) = (unsigned long)count_interruptes;
#endif
        //修正原始函数入口
        *((unsigned long*)(&entry_ptr[20])) = old_ISR_pointer[count];
        //将中断表项修改为刚创建的新的跳转模板
        __asm cli   //屏蔽中断
        idt_entries[count].LowOffset = (unsigned short)entry_ptr;
        idt_entries[count].HiOffset = (unsigned short)((unsigned long)entry_ptr>>16);
        __asm sti   //恢复中断
    }
    DbgPrint("Hook interrupt complete....");
}

void UnHookInterrupt()
{
    int i;
    IDTINFO idt_info;
    IDTENTRY* idt_entries;
    char _t[255];

    //加载idt_info
    __asm sidt idt_info;
    idt_entries = (IDTENTRY*)MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);

    for(i = START_IDT_OFFSET; i <= MAX_IDT_ENTRIES; i++)
    {
        _snprintf(_t,253,"interrupt id: %d  called %d times \n",i,g_i_count[i]);
        DbgPrint(_t);
    }

    DbgPrint("unhooking interrupt....");

    for(i = START_IDT_OFFSET; i <= MAX_IDT_ENTRIES; i++)
    {
        __asm cli   //屏蔽中断
        idt_entries[i].LowOffset = (unsigned short)old_ISR_pointer[i];
        idt_entries[i].HiOffset = (unsigned short)((unsigned long)old_ISR_pointer[i]>>16);
        __asm sti   //恢复中断
    }

    DbgPrint("unhooking interrupt complete....");

}

VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
    UnHookInterrupt();
    DbgPrint("OnUnload\n");
}

NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
    theDriverObject->DriverUnload  = OnUnload;
    HookInterrupt();
    return STATUS_SUCCESS;
}

加载运行后,使用PCHunter查看中断描述表,发现都被修改了。

image

然后通过反汇编可以看到,已经跳到我们自己写的汇编代码了。

image

第一个call是调用count_interruptes函数增加调用次数。

第二个jmp是跳转回原始的ISR入口地址。

卸载后可以看到每个中断的调用次数:

image

短短的几分钟内可以调用近5万次!!!

本文链接:http://www.alonemonkey.com/hook-all-idt.html