章节介绍
- Win7 x86 设置10-10-12分页.
- windows与Intel管理物理页规则,以及Windows内存分区.
- 10-10-12分页规则及其由来.
- 通过线性地址查找对应物理地址.
- 共享内存实现原理.
- 0地址访问错误原因,常量区无法修改原因,高2G内存R3访问失败起因于物理页属性限定.
- PDE,PTE属性探测,基址探测(MmIsAddressValid).
- 代码示例0线性地址挂物理页,0线性地址实现SHELLCODE.
0.Win7 x86设置10-10-12分页
CMD:关闭指令(开启10-10-12分页) bcdedit /set pae ForceDisable bcdedit /set nx AlwaysOff 开启指令(开启2-9-9-12分页)bcdedit /set pae forceEnablebcdedit /set nx OptIn 分页内核文件目录: C:\Windows\System32
x86下每个进程都有假造4GB内存空间(x86下线性地址宽度为32位4字节,2^32为4GB(线性地址有4G但未必都能访问 高2G内存为所有进程共享)),x86下假造地址分别为:
内存区范围空指针赋值区0x00000000 - 0x0000FFFF用户模式区 0x00010000 - 0x7FFEFFFF64KB禁入区0x7FFF0000 - 0x7FFFFFFF内核0x80000000 - 0xFFFFFFFFintel amd规定一个最小物理页为4KB(一个物理页4096byte遍历每一个字节2^12这也就是分页最后12由来)
Windows规定一个最小物理页为64KB.
1.10-10-12分页规则
x86下每个进程有4GB线性地址空间,但是有些地址无法访问,这是由于线性地址对应物理地址为空大概对应物理页属性限定.代码示例:MOV EAX, DWORD PTR DS:[0x12345678]有效地址: 0x12345678线性地址: DS.BASE + 0x12345678物理地址: 10-10-12分页 通过高31-22位定位PDE,通过21-12位定位PTE,通过PTE指向基址 + 11-00位定位页内偏移每个进程都有一个CR3(CR3是一个寄存器,每个核心一个) CR3指向一个物理页(4KB),10-10-12分页模式下CR3指向PDT分页规则物理页: 4KB(4096BYTE) 10: PDE(4BYTE),物理页(4KB),一张物理页可以存储 4096 / 4 (1024),要遍历每一个PDE 2^1010: PTE(4BYTE),物理页(4KB),一张物理页可以存储 4096 / 4 (1024),要遍历每一个PTE 2^1012: 物理页(4KB),要遍历每一个字节 2^1210-10-12线性地址上限: 1024 * 1024 * 4096 4GB10-10-12物理地址上限: 2^32(物理地址宽度) 4GB同一进程内相同物理页 MOV EAX, DWORD PTR DS:[0x12345FFF] MOV EAX, DWORD PTR DS:[0x12345EEE] 线性地址前20位(PDE PTE)决定在哪张物理页,后12位(OFFSET)决定页内偏移 PTE规则PTE可以没有物理页,且只能对应一个物理页.多个PTE也可以指向同一个物理页. 1.1 通过线性地址查找对应物理地址
#include #include int main(){ DWORD num = 0x64; printf("numaddr -> 0x%08x numdata -> 0x%08x \r\n",&num, num); system("pause"); printf("numaddr -> 0x%08x numdata -> 0x%08x \r\n",&num, num); system("pause"); return 0;}
有效地址为: 0x0012ff44
线性地址为: 0x0012ff44 + SS.Base(0)
线性地址转为二进制: 0000 0000 0001 0010 1111 1111 0100 0100
10: 00 0000 0000 0h PDE
10: 01 0010 1111 12Fh PTE
12: 1111 0100 0100 F44h OFFSET
通过Windbg手动检察:
1).通过!process 0 0查找进程CR3
DirBase = Cr3 = PDT.Base = 0x28e43000
*2).查找PDE(巨细4字节)(PDT(页目录表基址) + PDI(页目录项索引) 4)**
!dd(4Byte方式)检察物理地址
*3).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
4).查找线性地址对应物理地址(Physical Page + offset)
查找需要将PTE后12位抹除(为PTE属性)
5).修改物理页数据(!ed指令)
1.2 申请内存挂物理页过程
#include #include int main(){ PVOID lp = VirtualAlloc(NULL,0x64,MEM_COMMIT,PAGE_EXECUTE_READWRITE); if (!lp) { printf("VirtualAlloc Fail \r\n"); getchar(); } printf("mem addr -> 0x%08x \r\n",lp); system("pause"); memset(lp,0xFF,4); system("pause"); return 0;}
有效地址为: 0x001c0000
线性地址为: 0x001c0000 + SS.Base(0)
线性地址转为二进制: 0000 0000 0001 1100 0000 0000 0000 0000
10: 00 0000 0000 0h PDE
10: 01 1100 0000 1C0h PTE
12: 0000 0000 0000 0h OFFSET
通过Windbg手动检察:
1).通过!process 0 0查找进程CR3
DirBase = Cr3 = PDT.Base = 0x77990000
*2).查找PDE(巨细4字节)(PDT(页目录表基址) + PDI(页目录项索引) 4)**
!dd(4Byte方式)检察物理地址
*3).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
3).执行初始化内存代码
*4).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
6).查找线性地址对应物理地址(Physical Page + offset)
查找需要将PTE后12位抹除(为PTE属性)
使用virtualalloc函数在申请内存乐成情况下假如不初始化将不会被挂有效物理页
1.3 什么是共享内存?(多个线性地址共用一个或多个物理页)
进程A代码
#include #include int main(){ int num = 1; printf("numaddr -> 0x%08x numdata -> 0x%08x \r\n",&num , num); system("pause"); printf("numaddr -> 0x%08x numdata -> 0x%08x \r\n",&num , num); system("pause"); return 0;}进程B代码
#include #include int main(){ system("pause"); printf("0data -> 0x%08x \r\n",*(LPDWORD)(0 + 0xf44)); *(LPDWORD)(0 + 0xf44) = 0xffffffff; system("pause"); return 0;}1).打开进程A检察变量所对应物理地址
2).打开进程B给0线性地址挂物理页
3).执行进程B剩余代码
4).执行进程A剩余代码
进程B代码执行后将会同时修改进程A变量数据,由于此时指向同一张物理页且页内偏移相同
数据被乐成修改
2.10-10-12分页PDE PTE属性探测
测试代码使用内核函数MmIsAddressValid实现原理后文将分析
假如代码执行后不生效TLB(后需章节介绍)没有刷新,切换进程或打开新进程后在运行
2.1 P位
P = 1 有效的物理页
P = 0 无效的物理页
#include #include DWORD g_Data = 0x12345678;DWORD g_offs = (DWORD)&g_Data & 0xFFF;DWORD g_PDE = 0;DWORD g_PTE = 0;_declspec(naked) VOID ChangePTEAttribute(){ __asm { //获取g_Data线性地址 lea ecx, g_Data //获取PDE mov eax, ecx shr eax, 0x14 and eax, 0xFFC sub eax, 0x3FD00000 mov eax, [eax] mov g_PDE, eax //PDE->P test al, 1 jz END //获取PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov eax, [eax] mov g_PTE, eax //PTE->P test al, 1 jz END //获取0线性地址 mov ecx, 0 //0线性地址挂PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov edx, g_PTE mov [eax], edxEND: iretd }}int main(){ printf("INTERRUPT ADDR [0x%08x] \n",ChangePTEAttribute); printf("ADDR [0x%08x] DATA [0x%08x] OFFS [0x%03x] \n",&g_Data, g_Data, g_offs); system("Pause"); __asm { //通过中断门提权给0线性地址挂物理页 //eq 80b99500 0040ee00`00081005 INT 0x20 } //中断门执行返回后 0线性地址与g_Data变量地址对应的为同一张物理页 *(LPDWORD)(0 + g_offs) = 0xFFFFFFFF; printf("ADDR [0x%08x] DATA [0x%08x] OFFS [0x%03x] \n",&g_Data, g_Data, g_offs); system("Pause"); return 0;}
变量地址为: 0x00412030 转换为二进制 0000 0000 0100 0001 0010 0000 0011 0000
按照10-10-12分页格式转换:
10:00 0000 0001 001h PDE
10:00 0001 0010 012h PTE
12:0000 0011 0000 030h OFFSET
通过Windbg手动检察:
1).通过!process 0 0查找进程CR3
DirBase = Cr3 = PDT.Base = 0x0a207000
*2).查找PDE(巨细4字节)(PDT(页目录表基址) + PDI(页目录项索引) 4)**
!dd(4Byte方式)检察物理地址
*3).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
4).查找线性地址对应物理地址(Physical Page + offset)
查找需要将PTE后12位抹除(为PTE属性)
变量线性地址0x00412030对应物理地址为:#44c94030
0线性地址默认不可访问是由于没有挂有效物理地址
上述代码执行完毕后将0线性地址对应物理地址改为变量所对应物理地址,即0线性地址此时存在有效物理页
2.2 R/W位
R/W = 0 只读
R/W = 1 可读可写
#include #include CHAR* Data = "HHHHHH";DWORD g_PDE = 0;DWORD g_PTE = 0;_declspec(naked) VOID ChangePTEAttribute(){ __asm { //获取线性地址 mov ecx, Data //获取PDE mov eax, ecx shr eax, 0x14 and eax, 0xFFC sub eax, 0x3FD00000 mov eax, [eax] mov g_PDE, eax //PDE->P test al, 1 jz END //获取PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov edx, [eax] mov g_PTE, edx //PTE->P test dl, 1 jz END //页属性修改可读可写 or dl, 2 mov [eax], edxEND: iretd }}int main(){ printf("INTERRUPT ADDR [0x%08x] \n",ChangePTEAttribute); printf("ADDR [0x%08X] DATA [%s] \n",Data, Data); system("Pause"); __asm { //通过中断门提权修改页属性 //eq 80b99500 0040ee00`00081005 INT 0x20 } //默认情况Data[0]对应物理页属性为只读 通过修改可以改写 Data[0] = 'A'; printf("DATA [%s] PDE [0x%08x] PTE [0x%08x] \n",Data, g_PDE,g_PTE); system("Pause"); return 0;}
变量地址为: 0x0041010C 转换为二进制 0000 0000 0100 0001 0000 0001 0000 1100
按照10-10-12分页格式转换:
10:00 0000 0001 001h PDE
10:00 0001 0000 010h PTE
12:0001 0000 1100 10Ch OFFSET
通过Windbg手动检察:
1).通过!process 0 0查找进程CR3
DirBase = Cr3 = PDT.Base = 0x492e8000
*2).查找PDE(巨细4字节)(PDT(页目录表基址) + PDI(页目录项索引) 4)**
!dd(4Byte方式)检察物理地址
*3).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
4).查找线性地址对应物理地址(Physical Page + offset)
查找需要将PTE后12位抹除(为PTE属性)
变量线性地址0x0041010C对应物理地址为:#3fe1d10c
常量字符串不可以修改是由于物理页属性限定修改R/W即可改写
2.3 U/S位
U/S = 0 特权用户
U/S = 1 普通用户
2G以上是内核才气访问的原因是U/S位的设置题目,假如将内核的某个页设置为1就可以在R3访问了.
0 1 2是系统环可以访问系统页和用户页0环是特权级环1, 2环固然不是特权级环 但是是系统环 3环是用户环 可以访问用户页.
#include #include DWORD g_PDE = 0;DWORD g_PTE = 0;_declspec(naked) VOID ChangePTEAttribute(){ __asm { //获取线性地址 mov ecx, 0x80b99008 //获取PDE mov eax, ecx shr eax, 0x14 and eax, 0xFFC sub eax, 0x3FD00000 mov edx, [eax] test dl, 1 jz END or dl, 4 mov [eax], edx mov g_PDE, edx //获取PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov edx, [eax] test dl, 1 jz END //全局页修改G位 and edx, 0xFFFFFEFF //修改U/S位 or dl, 4 mov [eax], edx mov g_PTE, edxEND: iretd }}int main(){ printf("INTERRUPT ADDR [0x%08x] \n",ChangePTEAttribute); system("Pause"); __asm { //通过中断门提权修改页属性 //eq 80b99500 0040ee00`00081005 INT 0x20 } printf("PDE [0x%08x] PTE [0x%08x] \n",g_PDE,g_PTE); //高2G空间默认为特权权限访问修改页属性后用户权限也可以访问 printf("DATA [0x%08x] \n",*(LPDWORD)0x80b99008); system("Pause"); return 0;} 地址为: 0x80B99008 转换为二进制 1000 0000 1011 1001 1001 0000 0000 1000
按照10-10-12分页格式转换:
10:10 0000 0010 202h PDE
10:11 1001 1001 399h PTE
12:0000 0000 1000 008h OFFSET
通过Windbg手动检察:
1).通过!process 0 0查找进程CR3
DirBase = Cr3 = PDT.Base = 0x491ef000
*2).查找PDE(巨细4字节)(PDT(页目录表基址) + PDI(页目录项索引) 4)**
!dd(4Byte方式)检察物理地址
*3).查找PTE(巨细4字节)(PTT(页表基址) + PTI(页表项索引) 4)**
查找需要将PDE后12位抹除(为PDE属性)
4).查找线性地址对应物理地址(Physical Page + offset)
查找需要将PTE后12位抹除(为PTE属性)
线性地址0x80B99008对应物理地址为:#b99008
应用层不可以访问高2G内存是由于物理页属性限定修改U/S即可改写
2.4 PS位
只对PDE有意义
PS = 0 指向PTE
PS = 1 没有PTE 直接指向物理页 低22位是页内偏移 大页(2 ^ 22)4MB
2.5 A位
A = 0 未访问(读大概写)
A = 1 已访问(读大概写)
2.6 D位
D = 0 没有被写过
D = 1 被写过
3.PDT PTT基址
10-10-12分页模式1.页表被映射到了从0xC0000000到0xC03FFFFF的4M地址空间(1024PDE * 1024PTE * 4)2.在这1024个表中有一张特殊的表:页目录表3.页目录被映射到了0xC0300000开始处的4K地址空间0xc0300000 指向页目录表(PDT)0xc0000000 指向第一张页表(PTT)页目录表实在是一张特殊的页表,它是第0x300张页表。页目录表中每项PTE都指向一张页表,其中第0x300项指向了页目录表本身PDI(PDE INDEX) PTI(PTE INDEX)访问页目录表的公式:0xC0300000 + PDI * 4访问页表的公式:0xC0000000 + PDI * 4096 + PTI * 44.MmIsAddressValid(Win7 x86 10-10-12)
内核ntoskrnl.exe 10-10-12 分页
.text:0040A12A ; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress).text:0040A12A public _MmIsAddressValid@4.text:0040A12A _MmIsAddressValid@4 proc near ; CODE XREF: IopIsAddressRangeValid(x,x)+31↑p.text:0040A12A ; IopGetMaxValidMemorySize(x,x)+29↓p ....text:0040A12A.text:0040A12A VirtualAddress = dword ptr 8.text:0040A12A.text:0040A12A mov edi, edi ; 热补丁.text:0040A12C push ebp.text:0040A12D mov ebp, esp ; 提升栈底.text:0040A12F mov ecx, [ebp+VirtualAddress] ; ecx = 线性地址(Param).text:0040A132 call _MiIsAddressValid@8 ; MiIsAddressValid(x,x).text:0040A137 pop ebp ; 恢复栈底.text:0040A138 retn 4 ; 返回.text:0040A138 _MmIsAddressValid@4 endp.text:00489B98 ; __stdcall MiIsAddressValid(x, x).text:00489B98 _MiIsAddressValid@8 proc near ; CODE XREF: MiMakeSystemAddressValidSystemWs(x,x)+E↑p.text:00489B98 ; MiMakeSystemAddressValidSystemWs(x,x)+10C↑p ....text:00489B98 mov eax, ecx ; eax = 线性地址.text:00489B9A shr eax, 14h ; EAX右移20位 EAX >> 0x14.text:00489B9D and eax, 0FFCh ; (EAX >> 0x14) & 0xFFC(1111 1111 1100) == (EAX >> 0x16) * 4.text:00489BA2 sub eax, 3FD00000h ; sub 0x3DF00000 = add 0xC0300000.text:00489BA2 ; 0xC0300000是页目录表被映射的起始线性地址.text:00489BA7 mov eax, [eax] ; 取出PDE.text:00489BA9 test al, 1 ; 判断P位.text:00489BAB jnz short loc_489BB0 ; 非0跳转即有效页.text:00489BAD.text:00489BAD loc_489BAD: ; CODE XREF: MiIsAddressValid(x,x)+32↓j.text:00489BAD xor al, al ; al置0.text:00489BAF retn ; 返回.text:00489BB0 ; ---------------------------------------------------------------------------.text:00489BB0.text:00489BB0 loc_489BB0: ; CODE XREF: MiIsAddressValid(x,x)+13↑j.text:00489BB0 test al, al ; 判断PS.text:00489BB2 jns short loc_489BB7 ; al最高位是不为0跳转即小页跳转.text:00489BB4 mov al, 1 ; 大页al置1.text:00489BB6 retn ; 返回.text:00489BB7 ; ---------------------------------------------------------------------------.text:00489BB7.text:00489BB7 loc_489BB7: ; CODE XREF: MiIsAddressValid(x,x)+1A↑j.text:00489BB7 shr ecx, 0Ah ; ECX右移10位 ECX >> 0xA.text:00489BBA and ecx, 3FFFFCh ; (ECX >> 0xA) & 0x3FFFFC = PDI * 4096 + PTI * 4.text:00489BC0 sub ecx, 40000000h ; sub 0x40000000 = add 0xC0000000.text:00489BC0 ; 0xC0000000是页表被映射的起始线性地址.text:00489BC6 mov eax, [ecx] ; 取出PTE.text:00489BC8 test al, 1 ; 判断P位.text:00489BCA jz short loc_489BAD ; P = 0无效页跳转返回.text:00489BCC and al, 80h.text:00489BCE cmp al, 80h ; '?' ; //判断PAT位.text:00489BD0 setnz al ; PAT == 1 ZF = 1 AL置0 无效.text:00489BD0 ; PAT != 1 ZF = 0 AL置1 有效.text:00489BD3 retn.text:00489BD3 _MiIsAddressValid@8 endp5.代码示例
5.1 通过代码给0线性地址挂物理页并读写
#include #include //读写物理页地址LPDWORD g_lp = NULL;//记录PDE PTEDWORD dwPDE = 0;DWORD dwPTE = 0;//0环权限修改PDE PTEVOID _declspec(naked)Fun(){ __asm { pushad pushfd //获取物理页 mov ecx, g_lp //PDE mov eax, ecx shr eax, 0x14 and eax, 0x0FFC sub eax, 0x3FD00000 mov eax, [eax] mov dwPDE, eax //PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov eax, [eax] mov dwPTE, eax //获取0物理页 xor ecx, ecx //PDE mov eax, ecx shr eax, 0x14 and eax, 0x0FFC sub eax, 0x3FD00000 //备份对应线性地址 mov edx, eax mov eax, [eax] //判断是否存在PDE and eax, eax jnz Flag mov ebx, dwPTE mov [edx], ebxFlag: //PTE mov eax, ecx shr eax, 0xA and eax, 0x3FFFFC sub eax, 0x40000000 mov edx, dwPTE mov [eax], edx popfd popad retf }}int main(){ //Get Page g_lp = (LPDWORD)VirtualAlloc(NULL,0x100,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE); if (!g_lp) { MessageBox(NULL,"VirtualAlloc Fail !","ERROR",MB_OK|MB_ICONERROR); exit(0); } g_lp[0] = 0x12345678; printf("Data = %08x \n",g_lp[0]); system("Pause"); //CALL GATE printf("Function Addr = %08x \n",Fun); system("Pause"); CHAR calldata[6] = {0,0,0,0,0x48,0}; __asm { call fword ptr [calldata] } printf("PDE = %08X PTE = %08X \n",dwPDE,dwPTE); *(LPDWORD)0 = 0xFFFFFFFF; printf("Data = %08x \n",g_lp[0]); system("Pause"); return 0;}
5.2 0线性地址实现SHELLCODE
#include #include //PTE PDEDWORD dwCodePDE = 0;DWORD dwCodePTE = 0;DWORD dwZeroPDE = 0;DWORD dwZeroPTE = 0;//SHELLCODECHAR Data[] ={ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,//PUSH 0xE8,0x00,0x00,0x00,0x00, //CALL 0xc3 //ret};VOID _declspec(naked)Fun(){ __asm { //保留寄存器数据 pushad pushfd //获取CODE PAGE lea ecx, Data //PDI shr eax, 0x14 and eax, 0x00000FFC sub eax, 0x3FD00000 mov eax, [eax] mov dwCodePDE, eax //获取LP PTI shr ecx, 0xA and ecx, 0x003FFFFC sub ecx, 0x40000000 mov eax, [ecx] mov dwCodePTE, eax //0线性地址挂物理页 xor ecx, ecx mov eax, ecx //PDI shr eax, 0x14 and eax, 0x00000FFC sub eax, 0x3FD00000 mov edx, eax mov eax, [eax] mov dwZeroPDE, eax //判断PDE是否为0 and eax, eax jnz FLAG mov eax, edx mov ebx, dwCodePDE mov [eax], ebx mov dwZeroPDE, ebxFLAG: //修改0 PTE shr ecx, 0xA and ecx, 0x003FFFFC sub ecx, 0x40000000 mov edx, dwCodePTE mov [ecx], edx mov ecx, [ecx] mov dwZeroPTE, ecx //恢复寄存器数据 popfd popad retf }}int main(){ //获取函数地址 DWORD dwAddr = (DWORD)MessageBox; //修正SHELLCODE DWORD dwOffset = (DWORD)Data & 0xFFF; *(LPDWORD)&Data[9] = dwAddr - (dwOffset + 13); printf("Function Addr = %08x \n", Fun); system("Pause"); //CALL GATE CHAR calldata[6] = {0,0,0,0,0x48,0}; __asm { call fword ptr [calldata] call [dwOffset] } printf("Code PDE = %08x PTE = %08x \n",dwCodePDE,dwCodePTE); printf("Zero PDE = %08x PTE = %08x \n",dwZeroPDE,dwZeroPTE); system("Pause"); return 0;}
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |