什么是可读,可写,可执行。 线性地址和TLB的关系

C/C++的编程过程中应该都遇到过 0xC0000005,访问权限异常,当访问没有权限访问的页时候就会出现这个问题

经过这一段时间的学习,我发现我对可读可写可执行有了不一样的理解,从汇编层面

mov  ds:[0x12345678],eax   是把eax的值存放到 0x12345678线性地址对应的物理地址  这个线性地址对应的物理页既是可写

mov  eax,ds:[0x22222222]   这个线性地址0x22222222对应的物理页既是可读

想要理解什么是可读可写可执行首先需要理解页机制

而可执行的本质是所有对EIP寄存器修改,(既然是读EIP的修改肯定会给EIP一个线性地址)CPU必须对这个线性地址进行一些列的检查,比如如果这个线性地址

连物理页都没有肯定不能让你执行,返回一个0xC0000005异常,如果这个线性地址挂上了物理页,但是这个物理页没有执行权限属性,那也不能让你执行(xp中的vc++6.0的

数组可执行)是因为vc++6.0没有DEP(数据执行保护),而win10中的vs2017的不管是全局还是局部对应的物理页都没有执行权限

 

看似一条简单的指令在执行过程中需要检查的地方非常多

可写权限:上面的mov  ds:[0x12345678],eax   在执行的时候需要 检查0x12345678线性地址是有有物理页MmIsAddressValid, 如果没有物理页执行int e中断,判断是不是缺页,不是缺页返回一个0xC0000005(前提是Ring3,ring0就蓝了)缺页就把交换到文件中的页交换回来,如果没有缺页,这个线性地址有物理页的话,需要判断这个物理页是否有写的属性,

如果有写的属性才能把eax的值写入到 这个线性地址对应的物理页中,这条指令才算执行成功,而你在执行这条指令前,需要把EIP指向这条指令所在的地址,这时候需要判断这指令所在的地址所在的物理页有没有物理页,且有没有可执行权限只要有一个没有权限就挂了

可读权限:只要你的线性地址挂了物理就一定有可读权限

可读可写可执行:首先都是检查一个线性地址是否合法,如果是2-9-9-12分页需要通过这个线性地址对应的PDE  PTE 在访问对应的物理页,需要访问三次内存,这还是没有任何缺页的情况,没有访问的地址在不同PDE和PTE上的情况,否则需要访问更多次内存。

这样的话问题就来了,如果CPU没执行一条指令的话都必须检查这么多,如很多时候执行一段段程序(很多时候指令是连续的)每个执行一条指令都对线性地址都检查是否可执行,

这样效率低了因为很多时候指令地址都是在同一个物理页上的,于是CPU内部设置了一个TLB结构,TLB项纪录了当前进程的线性地址与之对应的物理页

 

TLB是CPU中的一张表
一般都有如下4组TLB
第一组,缓存一般页表(4KB字节页面)的指令页表缓存(Instruction-TLB)
第二组,缓存一般也表(4KB字节页面)的数据页表缓存 (Data  _TLB)
第三组,缓存大尺寸页表(2M/4M字节页面)的指令页表缓存
第四组,缓存大尺寸页表(2M/4M字节页面)的数据页表缓存

TLB一项如下 纪录了线性地址,这个线性地址与之对应的物理页地址, 和这个物理页的属性,并且纪录这这个物理页访问了多少次

LA(线性地址)    PA(物理地址)    ATTR(属性)    LRU(统计)

通过4组TLB可以发现,4KB页面有指令页缓存和数据页缓存,非常巧妙

有了TLB后如果我们所执行一段指令,首先取第一条指令的线性地址,在(4kb字节页面)的指令TLB   指令TLB中的项比较    比较LA(线性地址)如果找到了一个项与当前需要指令的线性

地址高20位相同的项(就说明这两个线性地址在同一个物理页(一个页4KB 线性地址的低12为页内偏移)),当找到高20线性地址相同的项,就不用在于拆分这个线性地址,判断有没有物理页啊,有没有权限,找到了高20位相同的项,此时比较ATTR是否有可执行属性没有就挂。如果没有指令缓存TLB中找到就在查找内存,看没有有挂上物理页,通过PTE对比有没有访问权限,如果有了就把这个线性地址和物理地址,以及页的属性做成一个记录写入指令缓存TLB中,执行下一面很大一段指令的时候很大概率就不用查找内存了,线性地址高20相同的直接查记录就ok了。

注意把记录写入的是指令缓存TLB之前是会改变EIP(这个也是可执行的本质)每一个指令周期都会改变EIP,为下一个条指令取指做准备  要改变EIP的值,得要给EIP一个地址(

但是这个地址不一定有效的必须要检查 ,就算有效还要检查有没有执行权限(这个是因为安全))jcc指令,ret  call jmp(jcc)等等指令执行也都会改变eip,那你给改成0那就挂了啊

所以修改eip值的时候如果没找到记录,且检查了是有效的,就把这个值和物理页,属性全都写入指令缓存TLB(为后续可能要执行同一个页的数据)

数据缓存TLB也是相同的原理不过是通过mov访问内存,push  pop (访问的就是esp中存储的线性地址)等等 把线性地址如果没有到数据缓存TLB中找到高20位相同的线性地址就,拆分这个地址是否有物理页,和对应物理页的权限,访问成功了就把这个线性地址和物理地址等信息加载到数据缓存TLB中

画了很大的力气想讲清楚可执行,相比于可读可写,可执行好像模糊一些,更难以让人理解一些

 

 

数据TLB和指令TLB都是这样的,如果满了会根据LRU(同级记录把访问最少的物理页删了(这个物理页的G位不为1)) ,G位为1的物理页不删

当进程切换的(就是CR3寄存器的值发送改动的时候)会把所有组TLB中物理页G位不为1的全都删了

验证TLB存在

首先开一个进程TBL Test开辟了两个页的内存,分别存储了06 和09并通过windug获得了

0x10000000   线性地址的PTE   1ee39067   存储的都是6

0x00A50000  线性地址的PTE  1f17a067    存储的都是9

#include "stdafx.h"
#include <Windows.h>

int Temp=0;
void  _declspec(naked) taolaoda(){
    __asm{
    
        mov  dword ptr ds:[0xC0000000],0x1f17a067             //0x390000      09
        mov  dword ptr ds:[0x0],0x11111111

#if 0        

        mov  eax,cr3                
        mov  cr3,eax                            //改变CR3的值

#endif

        mov  dword ptr ds:[0xC0000000],0x1ee39067             //0x10000000     06
        mov  eax,dword ptr ds:[0]
        mov  Temp,eax
        retf
    }
}
int main(int argc, char* argv[])
{
    char buf[6];
    *((WORD*)&buf[4])=0x48;
    printf("%p\n",taolaoda);

    __asm{
        call fword ptr buf;                                        //调用们我构建好了没写出来
    }
    printf("Temp=%p\n",Temp);
    getchar();
    return 0;
}

下面可以看到没有改变CR3寄存器,第一次访问0x0线性地址的时候会拆分这个地址并判断有没有写入权限,(067这个页是可读可写的),此时会往数据缓存TLB

加入一条记录有线性地址0与对应的物理页 0x1f17a000  的物理页的属性可读可写,访问次数

下面在访问这个0线性地址的时候,不会在拆分这个地址了能够在数据缓存TLB中找到记录,且是可读的有权限,会直接从0x1f17a000这个页开始读取4个字节 读取到的0x11111111前面写入的,因为第一次写入数据缓存TLB加入了记录,所以那个第二次给0线性地址挂上了另外的一个物理页,但是实际上操作的还是上一个物理页

 

修改CR3刷新TLB

mov cr3,eax刷新了cr3,此时相当于切换了进程(当然还是同一个进程,只要往cr3寄存器写入数据cpu就以为是切换进程就会清理TLB所有中G=0的项)

这里可以看到,第一次往0写入0x11111111后刷新了cr3,所以记录清除了在此挂上另外一个物理页(这个物理页每个字节存储的都是0x06)读取的就是0x06060606

 

把G位修改成1,可以看到当把G位修改成1后,发现修改cr3刷新TLB由于0x1f17a000物理页的G=1所以没有刷新,然后再次访问0线性地址,在数组缓存TLB中查找到了记录

记录的物理页是0x1f17a000实际上已经是0x1e39000,读取的还是0x1f17a000,所以读取的是0x11111111

上面这三个实验可以证明TLB存在了

在另外开的那个进0x390000线性地址对应的物理页前4个字节已经修改成了0x11111111,我在那个进程反复刷新还是现实0x09090909就是因为有记录,

通过INVLPG指令可以在TLB中清除一条记录,哪怕G=1的全局页都可以清理, 可以看到 两个页的PTE重新挂了不影响

第一个给0线性地址挂上的一个全局页,cr3刷新刷不掉,然后通过INVLPG删了这0线性地址的记录,然后在给0地址挂上了一个0x06687000物理页,

然后读取0地址的内存,(拆分的时候没有记录)会添加一个记录

通过上述实验就已经可以证明TLB存在了

希望大家能够结合TLB把可读可写可执行,以及比较过程理解清除

 

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值