页面异常捕获过滤:

原理:

实验中 容易出现的错误:

测试程序代码.c:

// 9_页面异常.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <stdio.h>
#include<stdlib.h>
#include<Windows.h>
// 定义一些内核地址宏;用来保存当前程序相关数据(cr3)
#define K_BOOL_READY   0x8003f3f0 // * 当前是否已经提交 cr3,变为准备接收异常状态
#define K_ESP 0x8003f3f4 // esp * 异常代码
#defineK_ESP_NEG4 0x8003f3f8 // esp - 4   * 异常地址 eip(产生异常的代码地址)
#define K_TARGET_CR30x8003f3ec // cr3       * 异常程序cr3
#define K_CR2 0x8003f3e8 // cr2       * 异常地址(产生页面异常页中的虚拟地址)
#define K_BOOL_NEW_EXPT 0x8003f3e4 // * 是否有新的异常


DWORD g_bReady = FALSE;
DWORD g_bNewExpt = FALSE;

DWORD g_pEIP = 0;
DWORD g_pExceptPvn = 0;
DWORD g_iCr3 = 0;
DWORD g_iErrCode = -1;

// 0x401040
void __declspec(naked) IdtEntry()
{
__asm mov eax, ds:[K_BOOL_READY];
__asm mov g_bReady, eax;
__asm
{
mov eax, cr3;
mov cr3, eax;
}
if (g_bReady != 1)
{

// 第一次运行还没有提交CR3数据:
__asm
{
mov eax, cr3;
mov ds : [K_TARGET_CR3], eax;
// 提交完成的标识。
mov eax, 0x1;
mov ds : [K_BOOL_READY], eax;
// 重置新异常信号;
mov eax, 0x0;
mov ds : [K_BOOL_NEW_EXPT], eax;
iretd;
}
}
else
{
// 已经提交 cr3 开始接收异常数据:

// ----* 检测是否有新的异常需要接收;
__asm mov eax, dword ptr ds : [K_BOOL_NEW_EXPT];
__asm mov g_bNewExpt, eax;
if (g_bNewExpt == 0x1)
{
// 如果 有新的异常
__asm
{
// 获取异常信息 --》 到本地全局,以便输出
mov eax, dword ptr ds : [K_ESP_NEG4];
mov g_pEIP, eax;
mov eax, dword ptr ds : [K_CR2];
mov g_pExceptPvn, eax;
mov eax, dword ptr ds : [K_TARGET_CR3];
mov g_iCr3, eax;
mov eax, dword ptr ds : [K_ESP];
mov g_iErrCode, eax;
// 接收之后 重置新异常信号;
mov eax, 0x0;
mov ds : [K_BOOL_NEW_EXPT], eax;
}

}

__asm
{
mov eax, cr3;
mov cr3, eax;
iretd;
}
}
}


void _declspec(naked) go()
{
__asm
{
int 0x20;
ret;
}
}


int main()
{
if ((DWORD)IdtEntry != 0x401040)
{
printf("Func addres is wrong !!");
Sleep(5000);
system("pause");
exit(-1);
}
while (1)
{
go();
//printf("%d\n", g_bNewExpt);
if (g_bNewExpt == 0x1)
{
printf("new error ***cr3: %p -- eip: %p -- errorcode:%p -- pfvn:%p\n", g_iCr3, g_pEIP, g_iErrCode, g_pExceptPvn);
g_bNewExpt = 0;
}

}

   
}

Hook IDT_E 回调函数 且 通过识别cr3 将相关信息过滤到 共识的共享数据区域 .c :

// 9_页面异常_过滤获取目标异常信息.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

// 定义一些内核地址宏;用来保存当前程序相关数据(cr3)
#define K_BOOL_READY   0x8003f3f0 // * 当前是否已经提交 cr3,变为准备接收异常状态
#define K_ESP 0x8003f3f4 // esp * 异常代码
#defineK_ESP_NEG4 0x8003f3f8 // esp - 4   * 异常地址 eip(产生异常的代码地址)
#define K_TARGET_CR30x8003f3ec // cr3       * 异常程序cr3
#define K_CR2 0x8003f3e8 // cr2       * 异常地址(产生页面异常页中的虚拟地址)
#define K_BOOL_NEW_EXPT 0x8003f3e4 // * 是否有新的异常

#define K_HOOK_IDT_E_CODE 0x8003F120      // * Hook 代码所在
#define K_HOOK_IDT_E_SRCCODE 0x80541450   // * Hook目标 所在

DWORD g_bReady = FALSE;
DWORD g_bNewExpt = FALSE;

DWORD g_pEIP = 0;
DWORD g_pExceptPvn = 0;
DWORD g_iCr3 = 0;
DWORD g_iErrCode = -1;
DWORD g_iCurCr3 = 0;


int  g_i = 0;
DWORD g_p = 0;
void  checkAndCap();
// -- int0x20 -- 0x401040
void __declspec(naked) HookIdt_0xe()
{
// hook
__asm
{
// 修改写保护 WP
//cli;//将处理器标志寄存器的中断标志位清0 ,不允许中断
mov eax, cr0
and eax, not 0x10000
mov cr0, eax

mov eax, 0x0;
mov ds : [K_BOOL_READY], eax;

// 原来的第一句 有7个字节;而我们push ret 只有6个字节 ;扩充到 7个字节 68 20 f1 03 80 C3 90;
// push 0x8003f120;
// ret
// nop
mov al, 0x68;
mov byte ptr ds : [K_HOOK_IDT_E_SRCCODE], al;
mov eax, 0x8003f120;
mov dword ptr ds : [K_HOOK_IDT_E_SRCCODE + 1], eax;
mov ax, 0x90c3;
mov word ptr ds : [K_HOOK_IDT_E_SRCCODE + 5], ax;


mov eax, cr0
or eax, 0x10000
mov cr0, eax
//sti;//将中断恢复

}

g_i = 0;
g_p = K_HOOK_IDT_E_CODE;

// 拷贝hook 代码带内核区
for (; g_i < 128; g_i++)
{
*(BYTE*)(g_p + g_i) = *(byte*)((DWORD)checkAndCap + g_i);
}

__asm
{
iretd;
}
}


// 0x401040 --
void _declspec(naked) checkAndCap()
{
__asm
{
push eax;
// 判断程序是否已经就位
mov eax, ds:[K_BOOL_READY];
cmp eax, 0x1;
jnz END;

// 如果 目标程序 已经提交cr3 那么 就可以开始捕获异常了;
mov eax,cr3;   // cr3不能用于比较的 参数
cmp eax, ds:[K_TARGET_CR3];
jnz END;

// 来到这一步,表明是目标的异常

mov eax, [esp + 8]; // 前面push 了一个 eax 我去 浪费时间··· esp + 0x4 --> esp+ 0x8;
mov ds : [K_ESP_NEG4], eax; // eip 异常地址
mov eax, cr2;
mov ds : [K_CR2], eax; // 异常读/写/访 的目标分页地址
mov eax, [esp+4];// esp-->esp +4 因为前面压入了 eax
mov ds : [K_ESP], eax; // 异常代码
// 给新的异常信号
mov eax, 0x1;
mov ds : [K_BOOL_NEW_EXPT], eax;

END:
pop eax;
mov  word ptr[esp + 2], 0 // 恢复 原来的执行
push 0x80541457;          // 返回之前hook 的下一句
ret
}
}

void _declspec(naked) go()
{
__asm
{
int 0x20;
ret;
}
}
int main()
{
if ((DWORD)HookIdt_0xe != 0x401040)
{
printf("HOOK出错了:%p", HookIdt_0xe);
Sleep(10000);
exit(-1);
}
if ((DWORD)checkAndCap != 0x4010c0)
{
printf("CHEC出错了:%p", checkAndCap);
Sleep(10000);
exit(-1);
}
go();
system("pause");



}

效果展示:


02-10 05:26