首页
社区
课程
招聘
[原创]通过C代码实现挂载NULL指针的PTE
2023-8-9 21:40 2135

[原创]通过C代码实现挂载NULL指针的PTE

2023-8-9 21:40
2135

实验:通过C代码实现挂载NULL指针的PTE(XP系统 2-9-9-12分页)

1、NULL未挂载时的PDPTE、PDE、PTE

PROCESS 8980bda0  SessionId: 0  Cid: 04a4    Peb: 7ffdc000  ParentCid: 0258
   DirBase: 0aa803c0  ObjectTable: e179a008  HandleCount:  18.
   Image: test_R0.exe

kd> !dq 0aa803c0 + 0 // PDPTE0
# aa803c0 00000000`00315801 00000000`00196801
# aa803d0 00000000`00197801 00000000`00094801
# aa803e0 00000000`bae71400 00000000`00000000
# aa803f0 00000000`00000000 00000000`00000000
# aa80400 00000000`bae71420 00000000`00000000
# aa80410 00000000`00000000 00000000`00000000
# aa80420 00000000`bae71440 00000000`00000000
# aa80430 00000000`00000000 00000000`00000000
kd> !dq 00315000 + 0 // PDE0
#  315000 00000000`13ea4867 00000000`00335867
#  315010 00000000`0bde3867 00000000`00000000
#  315020 00000000`00000000 00000000`00000000
#  315030 00000000`00000000 00000000`00000000
#  315040 00000000`00000000 00000000`00000000
#  315050 00000000`00000000 00000000`00000000
#  315060 00000000`00000000 00000000`00000000
#  315070 00000000`00000000 00000000`00000000
kd> !dq 13ea4000 + 0 // PTE0,此时空指针的PTE还未被挂载
#13ea4000 00000000`00000000 00000000`00000000
#13ea4010 00000000`00000000 00000000`00000000
#13ea4020 00000000`00000000 00000000`00000000
#13ea4030 00000000`00000000 00000000`00000000
#13ea4040 00000000`00000000 00000000`00000000
#13ea4050 00000000`00000000 00000000`00000000
#13ea4060 00000000`00000000 00000000`00000000
#13ea4070 00000000`00000000 00000000`00000000

2、通过特殊的线性地址,页表基址:0xC0000000,在代码中操作各个变量的PTE

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

int pPET_x;
int PTE_x_low;
int PTE_x_high;

// 读高2G内容:变量x的PTE
// 0x00401120
void __declspec(naked) ReadXPTE() {

__asm {
 // int 3;

 push eax;
 
 mov eax, dword ptr ds:[pPET_x];      // 取出全局变量的值
 mov eax, dword ptr ds:[eax];          // 取出PTE的高4字节
 mov dword ptr ds:[PTE_x_high], eax;    // 将PTE的高4字节复制给全局变量PTE_x_high
 
 mov eax, dword ptr ds:[pPET_x];       // 取出全局变量的值
 mov eax, dword ptr ds:[eax+4];        // 取出PTE的低4字节
 mov dword ptr ds:[PTE_x_low], eax;    // 将PTE的低4字节复制给全局变量PTE_x_low
 
 pop eax;
 iretd;
}
}


// 给空指针挂载PTE
// 0x00401050
void __declspec(naked) WriteNULLPTE() {

__asm {
 // int 3;

 push eax;
 
 mov eax, dword ptr ds:[PTE_x_high];   // 将PTE的高4字节挂载到NULL的高4字节位
 mov dword ptr ds:[0xC0000000], eax;

 mov eax, dword ptr ds:[PTE_x_low];   // 将PTE的低4字节挂载到NULL的低4字节位
 mov dword ptr ds:[0xC0000000+4], eax;
 
 pop eax;
 iretd;
}
}

int main(int argc, char* argv[])
{

int* ptr = NULL;

// 申请指定线性地址的变量x,0x0012f000的后3位为0是为了使得空指针和变量x的物理页偏移相等,指向同一个物理地址
int* ptr_x = (int*)(VirtualAlloc((LPVOID)0x0012f000, 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE));
*ptr_x = 0x12345678;

// 拆分变量x的线性地址 2-9-9-12分页
int lineAddress = (int)ptr_x;
int PDPTI_x = lineAddress >> 30;
int PDI_x = (lineAddress & 0x3FE00000) >> 21;
int PTI_x = (lineAddress & 0x1FF000) >> 12;
int offset_x = lineAddress & 0xFFF;

// 通过页表基址获取变量x的PTE的线性地址
pPET_x = 0xC0000000 + PDPTI_x * (2 << 21) + PDI_x * (2 << 12) + PTI_x * 8;

// 通过中断门提权,访问高2G地址0xC0000000
// gdt表中0x1偏移的段描述符段中DPL=0
// idt表中0x20偏移的中断描述符:0040EE00 00081120
__asm {
 int 0x20
}

// 将变量x的PTE挂载到NULL的PTE上
// gdt表中0x1偏移的段描述符段中DPL=0
// idt表中0x21偏移的中断描述符:0040EE00 00081050
__asm {
 int 0x21
}

// 读取NULL的值
printf("挂载PTE后空指针的值:%x\n", *ptr); // 运行结果:挂载PTE后空指针的值:12345678

getchar();

return 0;
}

3、NULL挂载后的PDPTE、PDE、PTE

// 程序重启过,CR3不同,但是实验效果是一致的。
PROCESS 89dfe020  SessionId: 0  Cid: 0564    Peb: 7ffd6000  ParentCid: 05b4
   DirBase: 0aa80360  ObjectTable: e10e9d48  HandleCount:  16.
   Image: test_R0.exe

kd> !dq 0aa80360 // PDPTE0  
# aa80360 00000000`238db801 00000000`2409c801
# aa80370 00000000`2805d801 00000000`268da801
# aa80380 00000000`bae71340 00000000`2bb89801
# aa80390 00000000`271ca801 00000000`29b47801
# aa803a0 00000000`bae713c0 00000000`00000000
# aa803b0 00000000`00000000 00000000`00000000
# aa803c0 00000000`bae713e0 00000000`00000000
# aa803d0 00000000`00000000 00000000`00000000
kd> !dq 238db000 // PDE0
#238db000 00000000`2001f867 00000000`1bdbb867
#238db010 00000000`1b69e867 00000000`00000000
#238db020 00000000`00000000 00000000`00000000
#238db030 00000000`00000000 00000000`00000000
#238db040 00000000`00000000 00000000`00000000
#238db050 00000000`00000000 00000000`00000000
#238db060 00000000`00000000 00000000`00000000
#238db070 00000000`00000000 00000000`00000000
kd> !dq 2001f000 // PTE0,此时空指针的PTE已经被挂载上了x的PTE
#2001f000 00000000`1ff74867 00000000`00000000
#2001f010 00000000`00000000 00000000`00000000
#2001f020 00000000`00000000 00000000`00000000
#2001f030 00000000`00000000 00000000`00000000
#2001f040 00000000`00000000 00000000`00000000
#2001f050 00000000`00000000 00000000`00000000
#2001f060 00000000`00000000 00000000`00000000
#2001f070 00000000`00000000 00000000`00000000

4、总结

本次实验通过C代码访问线性地址0xC0000000直接操作各个变量的PTE,通过位运算的方式直接操作地址运算,后续逆向分析内核文件ntkrnlpa.exe看看它是怎么判断一个线性地址是有效的。 本次实验遇到的问题: (1) 在访问0xC0000000时发现是高2G的地址,需要CPU提权后才能操作。 (2) 读取变量x的PTE发现是8个字节,需要两个4字节变量来分开保存,要注意高4字节和低4字节。 (3) 当直接定义变量x后挂载PTE,发现NULL取值不是x的值,这是因为二者的物理页偏移不同,因此通过virtualAlloc函数指定变量的地址,通过设置变量x地址的后3位为0,即可使得NULL和x指向同一个物理地址。 (4) 本实验在处理中断门时,是手动处理的函数地址和中断门描述符。鉴于这部分是低2G的地址,不需要提权就可以直接操作,因此本实验没有写相关代码。



[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2023-8-9 21:44 被ATrueMan编辑 ,原因: 格式
收藏
点赞2
打赏
分享
最新回复 (3)
雪    币: 19485
活跃值: (29158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-8-10 09:18
2
1
感谢分享
雪    币: 914
活跃值: (2188)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
万剑归宗 1 2023-8-10 09:39
3
0

雪    币: 2140
活跃值: (3538)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
R0g 2 2023-8-10 11:04
4
0

最后于 2023-8-10 11:05 被R0g编辑 ,原因:
游客
登录 | 注册 方可回帖
返回