11.10-10-12分页

目录

0.Win7设置10-10-12分页

1.10-10-12分页规则

2.10-10-12分页PDE PTE属性探测

<1>.P位

<2>.R/W位

<3>.U/S位

<4>.PS位

<5>.A位

<6>.D位

3.PDT PTT 基址

4.MmIsAddressValid(Win x86 10-10-12)

5.代码实例

<1>.通过代码给0线性地址挂物理页并读写

<2>.0线性地址实现SHELLCODE


0.Win7设置10-10-12分页

CMD:

关闭指令(开启10-10-12分页)                         
bcdedit /set pae ForceDisable  
bcdedit /set nx AlwaysOff      

开启指令(开启2-9-9-12分页)
bcdedit /set pae forceEnable
bcdedit /set nx OptIn 

1.10-10-12分页规则

11.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^10
10: PTE(4BYTE),物理页(4KB),一张物理页可以存储 4096 / 4 (1024),要遍历每一个PTE 2^10
12: 物理页(4KB),要遍历每一个字节 2^12

10-10-12线性地址上限: 1024 * 1024 * 4096 4GB
10-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也可以指向同一个物理页.  

2.10-10-12分页PDE PTE属性探测

测试代码使用内核函数MmIsAddressValid实现原理后文将分析

如果代码执行后不生效TLB(后需章节介绍)没有刷新,切换进程或打开新进程后在运行

11.10-10-12分页

11.10-10-12分页

<1>.P位

P = 1 有效的物理页

P = 0 无效的物理页

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

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], edx
	
END:
    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;
} 

11.10-10-12分页

变量地址为: 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

11.10-10-12分页

DriBase = Cr3 = PDT.Base = 0x0a207000h

2).查找PDE(大小4字节)(PDT(页目录表基址) + PDI(页目录项索引) * 4)

!dd(4Byte方式)查看物理地址

11.10-10-12分页

3).查找PTE(大小4字节)(PTT(页表基址) + PTI(页表项索引) * 4)

查找需要将PDE后12位抹除(为PDE属性)

11.10-10-12分页

4).查找线性地址对应物理地址(Physical Page + offset)

查找需要将PTE后12位抹除(为PTE属性)

11.10-10-12分页

变量线性地址0x00412030对应物理地址为:#44c94030

0线性地址默认不可访问是因为没有挂有效物理地址

上述代码执行完毕后将0线性地址对应物理地址改为变量所对应物理地址,即0线性地址此时存在有效物理页

11.10-10-12分页

<2>.R/W位

R/W = 0 只读

R/W = 1 可读可写

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

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], edx

END:
    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;
} 

11.10-10-12分页

变量地址为: 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

11.10-10-12分页

DriBase = Cr3 = PDT.Base = 0x492e8000h

2).查找PDE(大小4字节)(PDT(页目录表基址) + PDI(页目录项索引) * 4)

!dd(4Byte方式)查看物理地址

11.10-10-12分页

3).查找PTE(大小4字节)(PTT(页表基址) + PTI(页表项索引) * 4)

查找需要将PDE后12位抹除(为PDE属性)

11.10-10-12分页

4).查找线性地址对应物理地址(Physical Page + offset)

查找需要将PTE后12位抹除(为PTE属性)

11.10-10-12分页

变量线性地址0x0041010C对应物理地址为:#3fe1d10c

11.10-10-12分页

常量字符串不可以修改是因为物理页属性限制修改R/W即可改写

<3>.U/S位

U/S = 0 特权用户

U/S = 1 普通用户

2G以上是内核才能访问的原因是U/S位的设置问题,如果将内核的某个页设置为1就可以在R3访问了

0 1 2是系统环可以访问系统页和用户页0环是特权级环1、 2环虽然不是特权级环 但是是系统环 3环是用户环 可以访问用户页

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

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, edx

END:
    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

11.10-10-12分页

DriBase = Cr3 = PDT.Base = 0x491ef000h

2).查找PDE(大小4字节)(PDT(页目录表基址) + PDI(页目录项索引) * 4)

!dd(4Byte方式)查看物理地址

11.10-10-12分页

3).查找PTE(大小4字节)(PTT(页表基址) + PTI(页表项索引) * 4)

查找需要将PDE后12位抹除(为PDE属性)

11.10-10-12分页

4).查找线性地址对应物理地址(Physical Page + offset)

查找需要将PTE后12位抹除(为PTE属性)

11.10-10-12分页

线性地址0x80B99008对应物理地址为:#b99008

11.10-10-12分页

应用层不可以访问高2G内存是因为物理页属性限制修改U/S即可改写

<4>.PS位

只对PDE有意义

PS = 0 指向PTE

11.10-10-12分页

PS = 1 没有PTE 直接指向物理页 低22位是页内偏移 大页(2 ^ 22)4MB

11.10-10-12分页

<5>.A位

A = 0 未访问(读或者写)

A = 1 已访问(读或者写)

<6>.D位

D = 0没有被写过

D = 1被写过

3.PDT PTT 基址

11.10-10-12分页

10-10-12分页模式

1.页表被映射到了从0xC0000000到0xC03FFFFF的4M地址空间(1024PTT * 4096)
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 * 4

4.MmIsAddressValid(Win x86 10-10-12)

内核ntoskrnl.exe 10-10-12 分页

11.10-10-12分页

11.10-10-12分页

5.代码实例

<1>.通过代码给0线性地址挂物理页并读写

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

//读写物理页地址
LPDWORD g_lp = NULL;

//记录PDE PTE
DWORD dwPDE = 0;
DWORD dwPTE = 0;

//0环权限修改PDE PTE
VOID _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], ebx
	
Flag:
    //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;
}

11.10-10-12分页

<2>.0线性地址实现SHELLCODE

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

//PTE PDE
DWORD dwCodePDE = 0;
DWORD dwCodePTE = 0;

DWORD dwZeroPDE = 0;
DWORD dwZeroPTE = 0;

//SHELLCODE
CHAR 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, ebx
    
FLAG:
    //修改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;
} 

11.10-10-12分页

11.10-10-12分页

上一篇:按图索骥|Chapter5 网络空间安全专业与CISSP、CISP等认证有什么关系?


下一篇:CISP 考试教材《第 2 章 知识域:网络安全监管》知识整理