shadowWalker 原理:
接管 指定程序 的 执行页面异常、读写页面异常;然后 调用一下正常的 使其出现在快表;然后恢复到假的pte
------ 根据白皮书中 的error code 过滤出 执行、读写异常类型
相关:
将 402000 这个页面通过修改pte 设置为不存在(直接 给 p位置为 0);这样产生异常 我们接管
全程接管 读写、执行异常
读写异常 -- 不能交给系统默认处理;因为系统有虚拟地址的管理,他可能以为还没有映射该页面或者可能被置换到磁盘文件等,再次映射;但是还是不是我们想要的正确的。
所以我们接管。
只接管 402000 页面(具体根据项目//后来我的项目是 412000)
因为其他页面也会产生异常;如果想把该程序的都接收,那么得区分出来分别处理,这里我们只处理一个页面就行;原理类似。
利用 TLB 使得瞬间正常,然后又恢复异常状态;不然只接管了一次。
#define K REAL PTEO 0x8003f3e4#define K REAL PTE1 0x8003f3e0#define K FAKE PTEO 0x8003f3dc#define K FAKE PTE1 0x8003f3d8DWORD g esp;DWORD 9 esp 4;DWORD 9 cr2;//0x401000 void一dec lspec (naked) IdtEntry(){*(DWORD *)K REAL PTEO = PTE (0x402000)[0];*(DWORD *)K REAL PTE1 = PTE(0x402000)[1;dasirmov eax, cr3mov ds:[K TARGET CR3],eax}_asm {iretd#pragma code seg(" . my_ code")__ declspec (allocate(".my_ code ")) void gc#pragma code seg(".my_ code")_ dectspec (allocate(".my_ code ")) void mavold go() {_asmint 0x20//eq 8003f500 0040ee000081000void main()
在被接管得测试程序 一进入 0环 ,就保存 需要接管的 pte ;代码如上;
异常接管后;如果不给页面的话 ;它会一直死循环 页面异常;所以 我们给他假的页面
创建一个假页面:
#pragma section("data seg", read, write)_declspec(allocate("data seg")) DWORD FakePage[1024]; //405000
再把假页面给 内核;这样 我们在内核接管的时候;可以将这个假页面给请求者。
*(DWORD *)K_FAKE_PTEO = PTE (0x405000)[0];*(DWORD *)K_FAKE_PTE1 = PTE (0x405000)[1];
处理之后不能直接退出;得手动平 error code 这个栈位;因为中断系统只平默认的5个。
代码1 被隐藏目标程序:shadowWalker.cpp
// 9_页面异常.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include "pch.h"#include <stdio.h>#include<stdlib.h>#include<Windows.h>// 计算 pte 的宏 -- 针对29912#define PTE(y) ((DWORD *)(0xc0000000 + ((y>> 12)<< 3)))// 定义一些内核地址宏;用来保存当前程序相关数据(cr3)#define K_REAL_PTE0     0x8003f3e0     // * 真正得pte 【0】#define K_REAL_PTE1     0x8003f3e4       // * 真正得pte 【1】// 假页的PTE#define K_FAKE_PTE0     0x8003f3d0       // * 假页 pte  【0】#define K_FAKE_PTE1   0x8003f3d4     // * 假页 pte  【1】// j假#define K_TARGET_CR30x8003f3c0    // cr3       * 异常程序cr3DWORD g_bReady = FALSE;DWORD g_bNewExpt = FALSE;DWORD g_pEIP = 0;DWORD g_pExceptPvn = 0;DWORD g_iCr3 = 0;DWORD g_iErrCode = -1;void go();#pragma section("data_seg",read,write)__declspec(allocate("data_seg")) DWORD FakePage[1024] = {0};// 0x41d000//#pragma code_seg(".text") __declspec(allocate(".text"))void IdtEntry();// 0x401040void __declspec(naked) IdtEntry(){// 注册 int 0x21; -- FixBack() -- 0x401090*(DWORD*)0x8003f508 = 0x000810b0;*(DWORD*)0x8003f50c = 0x0040ee00;// 将pte数据提交//__asm int 3;*(DWORD*)K_REAL_PTE0 = PTE((DWORD)0x412000)[0];*(DWORD*)K_REAL_PTE1 = PTE((DWORD)0x412000)[1];*(DWORD*)K_FAKE_PTE0 = PTE((DWORD)0x41d000)[0];*(DWORD*)K_FAKE_PTE1 = PTE((DWORD)0x41d000)[1];//__asm int 3;g_pExceptPvn = PTE(0x412000)[0];g_pEIP = *(DWORD*)K_REAL_PTE0;PTE(0x412000)[0]= PTE(0x412000)[0]&0xfffffffe;// 关闭 p位 缺页异常!。__asm{    // 将当前cr3 数据提交    mov eax, cr3;    mov ds : [K_TARGET_CR3], eax;    //// 提交完成的标识。    //mov eax, 0x1;    //mov ds : [K_BOOL_READY], eax;    iretd;}}void __declspec(naked) FixBack(){__asm{    //int 3    mov eax, cr3;    mov cr3, eax;}__asm{    invlpg ds : [0x412000];// 书信 TLB}PTE(0x412000)[0] = *(DWORD*)K_REAL_PTE0;//PTE(0x412000)[1] = *(DWORD*)K_REAL_PTE1;__asm{    mov eax, ds:[0x412000];    //int 3;    push 0x3b;    pop fs;    iretd; // 返回。}}// 真实得代码页:  0x412000;五角星  放得位置 决定着 所在得虚拟位置 == 地址#pragma code_seg(".my_code") __declspec(allocate(".my_code"))int main();#pragma code_seg(".my_code") __declspec(allocate(".my_code"))void go();void _declspec(naked) go(){__asm{    int 0x20;    ret;}}int main(){__asm {    jmp L    ret// 这里会产生缺页中断 --- 其实这里是对下面的 缺页中断 响应    //00402005L :}if ((DWORD)IdtEntry != 0x401040){    printf("wrong addr: %p", IdtEntry);    printf("wrong addr: %p", FixBack);    exit(-1);}FakePage[0] = 0;go();__asm{    //int 3;}int i = 0;while (1) {    if (i == 2000)    {        __asm        {            int 0x21;            //inc i;                    }        printf("%d   : BF: %p AF:%p \n", i++, g_pEIP, g_pExceptPvn);        break;    }    printf("%d   : BF: %p AF:%p \n", i++,g_pEIP,g_pExceptPvn);    Sleep(1000);// 这里 Sleep完之后;不存在那个TLB 表中了会产生 缺页 中断;    }system(" pause");}//int main()//{//if ((DWORD)IdtEntry != 0x401040)//{//    printf("Func addres is wrong !!");//    Sleep(5000);//    system("pause");//    FakePage[0] = 0;//    exit(-1);//}//__asm//{//    jmp L;//    ret;//L://}////FakePage[0] = 0;// 这里访问一次使得可以 第一次可以不产生异常。////printf("%d\n", FakePage[0]);//go();//int i = 0;//while (1)//{//    i++;//    if (i % 10 != 0)//    {//        for (int j = 0; j < i; j++)//        {//            printf(".");//        }//        Sleep(50);//        continue;//    }//    else//    {//    i = 0;//    //    printf("runing...\n");//    Sleep(500);//    system("cls");//    }////}////system("pause");//}
代码2:注册和过滤处理目标页面异常消息
// 9_页面异常_过滤获取目标异常信息.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include "pch.h"#include<stdio.h>#include<stdlib.h>#include<Windows.h>// 计算 pte 的宏 -- 针对29912#define PTE(y) ((DWORD *)(0xc0000000 + ((y>> 12)<< 3)))// 定义一些内核地址宏;用来保存当前程序相关数据(cr3)#define K_REAL_PTE0     0x8003f3e0     // * 真正得pte 【0】#define K_REAL_PTE1     0x8003f3e4       // * 真正得pte 【1】// 假页的PTE#define K_FAKE_PTE0     0x8003f3d0       // * 假页 pte  【0】#define K_FAKE_PTE1   0x8003f3d4     // * 假页 pte  【1】// j假#define K_TARGET_CR30x8003f3c0    // cr3       * 异常程序cr3#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 -- 0x401040void __declspec(naked) HookIdt_0xe(){// hook __asm{    // 修改写保护 WP    //cli;//将处理器标志寄存器的中断标志位清0 ,不允许中断    mov eax, cr0    and eax, not 0x10000    mov cr0, 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 < 256; g_i++){    *(BYTE*)(g_p + g_i) = *(byte*)((DWORD)checkAndCap + g_i);}__asm{    iretd;}}// 0x401040  -- void _declspec(naked) checkAndCap(){__asm{    pushad;    // 判断是否是 目标程序    mov eax, cr3;   // cr3不能用于比较的 参数    cmp eax, ds:[K_TARGET_CR3];    jnz PASS;    //***** 到这里 说明是新的 目标程序的异常    // 再过滤指定区段得异常        mov eax, cr2;    shr eax, 0xc;    cmp eax, 0x412;        jnz PASS;    // 在这里需要拦截不能交给系统继续处理    mov eax, [esp + 0x20];// 因为前面push ad  ;error code;    test eax, 0x10;//如果是    jnz EXEC;    jmp DATA;DATA:    // 修改PTE  读写操作全部移动到假页;} PTE(0x412000)[0] =  *((DWORD*)K_FAKE_PTE0); //PTE(0x412000)[1] =  *((DWORD*)K_FAKE_PTE1);   __asm {     mov eax, ds:[0x412000]; };// 使得加入快表-- 这次得异常得以执行; PTE(0x412000)[0] = PTE(0x412000)[0] & 0xfffffffe;// 这样使得p位为0;会继续产生异常交给我们接管; //PTE(0x412000)[1] = 0;// 这样使得p位为0;会继续产生异常交给我们接管; __asm {     jmp DEALED; EXEC: } // 修改pte 将执行操作 移动到真页 PTE(0x412000)[0] = *(DWORD*)K_REAL_PTE0; //PTE(0x412000)[1] = *(DWORD*)K_REAL_PTE1; // 调用代码 使得进入 itlb  -- 指令快表--使得这次能得以处理 __asm {     mov eax, 0x00412012;     call eax; } // 继续使得p 位 为 0  -- 后面继续产生 缺页 我们接管。 PTE(0x412000)[0] = PTE(0x412000)[0] & 0xfffffffe; //PTE(0x412000)[1] = 0; __asm {DEALED:    popad;    add esp, 4;// 因为有一个 errorcode!!;这个得手动平    iretd;PASS:    popad;    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 != 0x4010b0){    printf("CHEC出错了:%p", checkAndCap);    Sleep(10000);    exit(-1);}go();system("pause");}





