前言
本次实验我们将简单的构造一个页面异常的处理函数,利用之前的Hook技术将相应代码写入GDT
中,为我们进程调用提供处理函数以及相关全局变量,通过实验中的一些现象来进行了解页面异常
实验过程
我们首先需要做的是构造一个写入代码到GDT
中的程序,以及将我们的Hook
代码进行写入目的地址处
我们尝试在地址:805454A8
处进行写入我们的代码,将用来处理中断服务的函数写入到GDT
中
1 2 3 4 5
| p = (char*)0x8003f120; for (int i = 0; i < 128; i++) { *p = ((char*)GlobalFunc)[i]; p++; }
|
在对应处理过程中,我们保存程序产生页异常时候的eip
、esp
、cr2
信息,将其同样保存在GDT
中,为我们程序后续读取做准备
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
| #define K_ESP 0x8003f3f4 #define K_ESP_4 0x8003f3f0 #define Addr_CR3 0x8003f3ec #define Addr_CR2 0x8003f3e8
void __declspec(naked) GlobalFunc() { __asm { push eax mov eax, cr3 cmp eax, ds : [Addr_CR3] jne _end
; 对于页面异常会压入一个 error code ; 之后顺序压入 eip cs eflags esp ss ; eip 指向的是触发异常的指令地址 ; cs 指向的是触发异常的指令所在的段 ; esp 指向的是触发异常时的 esp ; cr2 指向的是触发异常的地址
mov eax, ds : [esp + 4] ; 获取压入的 eip mov ds : [K_ESP] , eax mov eax, ds : [esp + 8] ; 获取压入的 cs mov ds : [K_ESP_4] , eax mov eax, cr2 ; 获取cr2 mov ds : [Addr_CR2] , eax _end : pop eax mov word ptr[esp + 2], 0 ; 源_KiTrap0E中开始代码 push 0x805454af ; 目标跳转地址 ret } }
|
而在Hook
部分我们构造一个push ret
的结构来实现跳转,将原代码从_KiTrap0E
中进行直接返回,到我们的目的地址
1 2
| 0x0: push 0x8003f120 0x5: ret
|
此处跳转目标为我们构建的Hook
函数具体内容,同时我们为了能够在内核进行修改需要关闭写保护,写入后开启保护,避免触发崩溃而导致蓝屏
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
| // hook part __asm { mov eax, 0xffffffff mov ds : [Addr_CR3] , eax; 全局变量Cr3初始化为 0xffffffff
; 关闭写保护 mov eax, cr0 and eax, ~0x10000 mov cr0, eax
; Hook main mov eax, 0x03f12068 mov ds : [0x805454A8] , eax mov ax, 0xC380 mov ds : [0x805454AC] , ax
xor eax, eax mov ds : [Addr_CR2] , eax mov ds : [K_ESP] , eax mov ds : [K_ESP_4] , eax
; 开启写保护 mov eax, cr0 or eax, 0x10000 mov cr0, eax }
|
至此我们的Hook
程序已经完成对应的工作,接下来需要实现的是对GDT
中保存的信息进行读取,代码如下:
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
| #include <stdio.h> #include <Windows.h>
#define K_ESP 0x8003f3f4 #define K_ESP_4 0x8003f3f0 #define Addr_CR3 0x8003f3ec #define Addr_CR2 0x8003f3e8
DWORD g_esp; DWORD g_esp_4; DWORD g_cr2;
void __declspec(naked) IdtEntry() { __asm { mov eax, cr3 mov ds:[Addr_CR3], eax
mov eax, ds:[K_ESP] mov g_esp, eax
mov eax, ds:[K_ESP_4] mov g_esp_4, eax
mov eax, ds:[Addr_CR2] mov g_cr2, eax
xor eax, eax mov ds:[K_ESP_4],eax
iretd } }
#pragma code_seg(".mycode") __declspec(allocate(".mycode")) void Crash(); #pragma code_seg(".mycode") __declspec(allocate(".mycode")) void main();
void Crash() { __asm { int 0x20 } }
int main() { if ((DWORD)IdtEntry != 0x00401040) { printf("IdtEntry : %p\n", IdtEntry); return 0; } while (1) { Crash(); if (g_esp_4) { printf("eip: %p, errorNo: %p, cr2: %p \n", g_esp_4, g_esp, g_cr2); } Sleep(1000); } return 0; }
|
我们直接取GDT
中数据即可,同时我们构建一个新的节,来保存我们的Crash
以及main
函数,让其处于页边界上,便于我们进行保护以及管理
成功运行后我们可以看到其已经捕获了对应页面异常信息,同时将其进行输出
此时我们成功的获取到了对应页异常信息目标的地址,异常号,以及发生异常的代码已经成功的实现了对页异常的Hook
总结
利用之前所使用的inlineHook
的方式,我们将对应地址的写保护进行关闭,在这之后替换为我们自己的push ret
结构跳转到我们的执行函数中,之后获取到相应的堆栈信息,将其保存在GDT
中,随后打印,完成此次实验。