VT EPT的科普文章

[复制链接]

该用户从未签到

759

主题

763

帖子

4660

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4660
跳转到指定楼层
楼主
发表于 2018-4-22 16:35:07 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

想要查看内容赶紧注册登陆吧!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
下面开始正题。(这些都是我自己摸索学习感悟到的,如有不对的欢迎大家喷我,指正讨论)
硬件虚拟化,到底什么意思,虚拟化什么东西。VT其中一个功能虚拟化内存。虚拟化内存什么意思,比如你的PC物理电脑实际上只有8G物理内存,但是用了虚拟化内存这个功能后,可以让你的操作系统(这里就用WINDOWS来代替操作系统)使用超过8G的物理内存。可以把你的物理内存隐藏起来让别人访问不到,包括操作系统也访问不到,当然也包括杀毒软件访问不到或者访问到的是你伪造的内存。 进一步讲,可以让你一台PC物理机同时运行多个操作系统,就像VMWARE一样。多个操作系统之间的物理内存是隔离的。其实简单点讲就是,1号操作系统访问物理内存0X12345678 和 2号操作系统访问物理内存0X1245678里面的内容可以不一样。 这是怎么做到的?  其实原理挺简单的。在WINDOWS里面为什么多个进程访问同一个地址,但是里面的内容可以做到不一样,两个进程都执行 mov eax,0x40001000,然而取出来的地址会不一样。为什么能做到这点,其实执行这条汇编指令的时候,0x40001000这个地址并不是真正的物理地址,那么真正的物理地址是什么呢,真正的物理地址是需要经过MMU(是个硬件)转换过的。怎么转换的呢,http://bbs.pediy.com/showthread.php?t=203391&highlight=物理+理地+地址------大家可以参考这篇文章。其实VT EPT功能就是把物理内存虚拟化了。就是把经过虚拟地址转换过后的物理地址,还需要再经常EPT机制再转换一次,从而实现,物理地址的虚拟化。下面说下这是怎么做到的。INTEL是怎么设计的。虚拟地址转换成物理地址,我们都知道是经过CR3所指向一块内存(可是理解成数组),多大4096字节。(为了便于新手理解其他页面大小这里暂不考虑)。其实EPT机制里面也有个类似CR3的寄存器,就是这个功能,开始转换地址的首地址EPTP。这个EPTP大家可以简单理解成CR3寄存器。这个虚拟地址到物理地址的转换是在后台,硬件偷偷在后台的完成的,我们完全感觉不到,当然我们的虚拟机的物理地址转换成真正的物理地址也是在硬件层次,后台偷偷完成的。我们完全感觉不到。但是怎么完成的。其实它是通过查表完成的。关键就在于这个表,我们把这个表初始化好了,构建好了,CPU就可以自动后台查找这个表运行转换功能了。

先上一 个图,突然发现要讲明白还真不容易呀,先休息下吧,吃饭了,
先看下这个表要怎么构建,CPU会怎么去用这个表,EPT扩展页表的功能。我们目前要做的就是把这个EPT给构建起来,啥功能都没有,什么意思呢,就是物理地址0X87654321 经过这个表转换之后还是 变成0x87654321(当然想变成什么地址你可以自己构建)。这里我都是以64位系统为例为讲的。
看下INTEL是怎么设计,怎么查找这个表的,首先硬件会从EPTP指向的一块内存开始,查找,这块内存你可以自己申请好4096字节,把这个起始地址,在VT初始化的时候赋值给VT的某个字段。因为是64位,所以是512项,一项占八个字节,相当于ULONG64 EPTP[512-1]里面有512项,每一项都是一个地址,这个地址指向另外一块内存,当然这块内存也是4096字节,里面也是512项的64位地址,就这样总共下去有四层,数组里面的每一项指向另外一块数组的首地址。反回来再说说,到底是取这个数组的哪一项呢,INTEL是这么设计的,把64位的物理地址分成5段,0-11位算一段,12-20位算一段,21-29位算一段,30-38位算一段,39-47位算一段。我们这里先无视0-12位,其他四个段都是9位组成,四个段,刚好对应四层表,就是说,每一段独立取出来,当作这个数组的页表的序列。看INTEL手册吧,看书的时候,感觉书上写的不清楚,论到自己写的时候,才感觉,要把这个意思表达出来还真不容易,
先把上面0x87654321 转换成二进制10000111011001010100001100100001,再把这段二进制分成五段,怎么分,从低位12,9,9,9,9 多余的位暂且无视,感觉用视频讲解会好很多,这里我试着尽量用文字表达清楚。
000000000 000000010        000111011        001010100        001100100001 分成这样五段,然后再把这五段二进制分别独立转换成十六进制0 0x2  0x3B 0x54 0x321 我们先看前面四段,0   2   3B  54   先看这个零代表什么意思,零就代表上面我们申请的内存页的第一项,就是EPTP首地址里面的第一项的内容。取出里面的内容,然后看图
这个地址fffffa80`01878000 就是我申请的4096字节大小的内存的首地址,你会发现只有第一项有内容,其余的零,因为我们实际物理内存并没有那么大,所有只需要填下第一项就行了。第一项里面的值也是一个地址,这个地址也是一个页4096字节大小的首地址,继续看图

图里可以看出也是有512项(注意这里存放的都是物理地址,然后无视最后位的数字7,看作零,7代表什么意思我等下再说),每一项又指向一块4096大小的内存的首地址,可以看出我只初始化了8项,这8项代表8G,8G是怎么算出来的,其实这里的每一项代表一个G的物理内存,

先了解虚拟地址怎么转换成物理地址的就可以看懂我发的图了.
ULONG64* ept_PML4T;
_Use_decl_annotations_ ULONG64* MyEptInitialization()
{
        PAGED_CODE();
        PHYSICAL_ADDRESS FirstPtePA, FirstPdePA, FirstPdptePA;
// 下面这个4096大小内存就是EPTP了,在VT初始化的时候把这个地址赋值给某个段,看代码,
        ept_PML4T = (ULONG64 *)(ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, kHyperPlatformCommonPoolTag));
        if (!ept_PML4T) { return 0; }
        RtlZeroMemory(ept_PML4T, PAGE_SIZE);
        ULONG64* ept_PDPT = (ULONG64 *)(ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, kHyperPlatformCommonPoolTag));
        if (!ept_PDPT) { return 0; }
        RtlZeroMemory(ept_PDPT, PAGE_SIZE);
        FirstPdptePA = MmGetPhysicalAddress(ept_PDPT);
        *ept_PML4T = (FirstPdptePA.QuadPart) + 7;
        for (ULONG64 a = 0; a< 8; a++)
        {
                ULONG64* ept_PDT = (ULONG64 *)(ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, kHyperPlatformCommonPoolTag));
                if (!ept_PDT) { return 0; }
                RtlZeroMemory(ept_PDT, PAGE_SIZE);
                FirstPdePA = MmGetPhysicalAddress(ept_PDT);
                *ept_PDPT = (FirstPdePA.QuadPart) + 7;
                ept_PDPT++;
                for (int b = 0; b < 512; b++)
                {
                        ULONG64* ept_PT = (ULONG64 *)(ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, kHyperPlatformCommonPoolTag));
                        if (!ept_PT) { return 0; }
                        RtlZeroMemory(ept_PT, PAGE_SIZE);
                        FirstPtePA = MmGetPhysicalAddress(ept_PT);
                        *ept_PDT = (FirstPtePA.QuadPart) + 7;
                        ept_PDT++;
                        for (int c = 0; c < 512; c++)
                        {
                                *ept_PT  = (a * (1 << 30) + b * (1 << 21) + c * (1 << 12) + 0x37);                                
                                ept_PT++;
                        }
                }
        }
        return ept_PML4T;        
}
分享到:  QQ好友和群QQ好友和群
收藏收藏
回复

使用道具 举报

快速回复高级模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表