• Windows虚拟地址转物理地址(原理+源码实现,附简单小工具)


     

                                                                                                               By Lthis

    上个月就想写了,一直没时间...网上大概搜了一下,原理与操作倒是一大堆,一直没看到源码实现,总得有人动手,这回轮到我了。东西写得很烂,请大牛勿喷。一直觉得靠源码的方式驱动学习是非常好的一种学习方法,比较直观!声明一下,本教程只有讨论开启PAE与关闭PAE两种,至于PSE是否开启没有管...我的虚拟机默认PSE貌似是开启滴?不知是不是写的小工具有问题....对于x64下的等我有时间再写吧。

    东西都上传在压缩包中了,Codes文件夹下是工程源码,Demo文件夹下是测试案例,Tool文件夹放的是小工具的Demo和源码。

    我的环境:开发环境(win7 sp1 x64 + vs2013社区版 update5 + wdk8.1

          测试环境(vm10 + win7 sp1 x86

    一、先说说未开启PAE的情况,祭出intel手册的经典图例:

    这幅图就是虚拟地址转为物理地址的原理图(4k页面),看图说话,用伪代码描述一下:

    1.Directory Entry(PDE)     = PDBR[Directory];

    2.Page-Table Entry(PTE) = PDE + Table * 4;

    3.Physical Address  = PTE + Offset;

    由上可知,Linear Address(线性地址)中的DirectoryTable其实就是个索引,在未开启PAE的情况下,PDEPTE均是32bit4字节,所以要Table*4),以上只是原理上的描述,实际上,PDEPTE的后3位是属性值,所以需要把后3位抹掉。

    下边上关键代码,基本都步骤都写了注释了,有需要的可以封装成函数。此外,本段代码只是测试用,写的很不规范,比如,在调用MmMapIoSpace应该调用MmUnMapIoSpace释放内存。

                // 得到ring3传入的虚拟地址
                size_t* pOutAddress = (size_t*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
                VIRTUAL_ADDRESS virtualAddress = { 0 };
                virtualAddress.ulVirtualAddress = *pOutAddress;
                ULONG pdbr;
    
                _asm{
                    mov eax,  cr3;
                    mov pdbr, eax;
                }
                
                PHYSICAL_ADDRESS phyAddress = { 0 };
                phyAddress.LowPart = pdbr;
                PULONG pPdbr = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                KdPrint(("pdbr = 0x%08X, 映射后的地址0x%p
    ", pdbr, pPdbr));
                
                // pPdbr[ulDirBaseIdx] 页目录项
                ULONG ulDirBaseIdx = virtualAddress.stVirtualAddress.dirBaseIndex;
                ULONG ulDirIdx = virtualAddress.stVirtualAddress.dirIndex;
                KdPrint(("第一级,已找到页目录所在项:pPdbr[%d]:0x%08X", ulDirBaseIdx,pPdbr[ulDirBaseIdx]));
                ULONG ulDir = pPdbr[ulDirBaseIdx] & 0xFFFFF000;            // 抹去后3位得到真正的页目录项
    
    
                ULONG ulDirPlus = ulDir + ulDirIdx * 4;                    // 页表项
                phyAddress.LowPart = ulDirPlus;
                PULONG pDirPlus = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                KdPrint(("第二级,已找到页表项:ulDirPlus = 0x%08X, 映射后的地址0x%p
    ", ulDirPlus, pDirPlus));
                ULONG ulPageTable = *pDirPlus & 0xFFFFF000;                // 抹去后3位得到真正的页表项
    
                // 得到物理地址
                ULONG ulPhyAddress = ulPageTable + virtualAddress.stVirtualAddress.offset;
    
                // 映射为虚拟地址,获取其值进行验证
                phyAddress.LowPart = ulPhyAddress;
                PWCHAR pPhyAddress = (PWCHAR)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                KdPrint(("虚拟地址:0x%08X, 对应物理地址:0x%08X, Value:%S
    ", *pOutAddress, ulPhyAddress, pPhyAddress));
    
                // 传出对应物理地址
                *pOutAddress = ulPhyAddress;

    二、开启PAE的情况

    同样是4k页面的,伪代码描述如下:

    1.Dir.Pointer Entry(PDPTE)  = PDPTR[Directory Pointer];

    2.Director Entry(PDE)  = PDPTE + Directory * 0x8;

    3.Page-Table Entry(PTE)  = PDE + Table * 0x8;

    4.Physical Address  = PTE+Offset;

    在开启PAE的情况下,PDEPTE均是64bit8字节,所以要*8),同样PDEPTE的后3位是属性值,所以需要把后3位抹掉。

    关键代码如下:

                // 得到传入的ring3层虚拟地址
                size_t* pOutAddress = (size_t*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
                VIRTUAL_ADDRESS virtualAddress = { 0 };
                virtualAddress.ulVirtualAddress = *pOutAddress;
                ULONG pdbr;
    
                // 得到页目录指针物理地址
                _asm{
                    mov eax,  cr3;
                    mov pdbr, eax;
                }
                
                // 映射为虚拟地址以便取值
                PHYSICAL_ADDRESS phyAddress = { 0 };
                phyAddress.LowPart = pdbr;
                PULONG pPdbr = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                KdPrint(("pdbr = 0x%08X, 映射后的地址0x%p
    ", pdbr, pPdbr));
                
                // 定位页目录指针表并获取页目录表物理页地址
                // ulDirAddress 为页目录表物理页地址
                ULONG ulPointerIdx = virtualAddress.stVirtualAddress.dirPointer;
                ULONG ulDirBaseAddress = pPdbr[ulPointerIdx];
                ulDirBaseAddress &= 0xFFFFF000;            // 中间物理地址
    
                // 定位页表项
                ULONG ulDirAddress = ulDirBaseAddress + virtualAddress.stVirtualAddress.dirIndex * 0x8;
                phyAddress.LowPart = ulDirAddress;
                PULONG pPageTable = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                ULONG ulPageTable = *pPageTable;
                ulPageTable &= 0xFFFFF000;                 // 中间物理地址
    
                // 定位物理页面
                ulPageTable += virtualAddress.stVirtualAddress.tableIndex * 0x8;
                phyAddress.LowPart = ulPageTable;
                PULONG pPageBase = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                ULONG ulPageBase = *pPageBase;
                ulPageBase &= 0xFFFFF000;
    
                // 得到物理地址
                ULONG ulPhyAddress = ulPageBase + virtualAddress.stVirtualAddress.offset;
                
                // 映射为虚拟地址,获取其值进行验证
                phyAddress.LowPart = ulPhyAddress;
                PWCHAR pPhyAddress = (PWCHAR)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);
                KdPrint(("虚拟地址:0x%08X, 对应物理地址:0x%08X, Value:%S
    ", *pOutAddress, ulPhyAddress, pPhyAddress));
    
                // 传出对应物理地址
                *pOutAddress = ulPhyAddress;
    
                pIrp->IoStatus.Information = cout;

    以上代码步骤是参考安于此生的文章写的,看不懂的可以先看看安于此生的文章《启用PAE后虚拟地址到物理地址的转换

    另附上小工具源码,该工具用于检测系统是否开启PAEPSE等。

    #define BUFFERSIZE    0x3000
    char g_szMemInfo[BUFFERSIZE] = { 0 };
        
    // 以下code在 DriverEntry 中
        
        DWORD dwPE  = 0;                // Protection Enable    cr0[0]
        DWORD dwWP  = 0;                // Write Protect        cr0[16]
        DWORD dwPG  = 0;                // Paging                cr0[31]
        DWORD dwPAE = 0;                // 物理地址扩展            cr4[5]
        DWORD dwPSE = 0;                // Page Size Extension    cr4[4]
        DWORD dwCr0 = 0;
        DWORD dwCr4 = 0;
    
        // 注册卸载函数
        pDriverObj->DriverUnload = driverUnload;
    
        _asm{
            pushad;
            mov eax, cr0;
            mov dwCr0, eax;
    
            // PE标志位
            and eax, 0x01;
            mov dwPE, eax;
            mov eax, cr0;
    
            // WP标志位
            and eax, 0x10000;
            mov dwWP, eax;
            mov eax, cr0;
    
            // PG标志位
            and eax, 0x80000000;
            mov dwPG, eax;
    
            // PAE
            //mov eax, cr4; 机器码如下
            _emit 0x0F;
            _emit 0x20;
            _emit 0xE0;
            mov dwCr4, eax;
            and eax, 0x20;
            mov dwPAE, eax;
            
            // PSE
            _emit 0x0F;
            _emit 0x20;
            _emit 0xE0;
            and eax, 0x10;
            mov dwPSE, eax;
    
            popad;
        }
        
        KdPrint(("PE  = 0x%08X
    ",dwPE));
        KdPrint(("WP  = 0x%08X
    ",dwWP));
        KdPrint(("PG  = 0x%08X
    ",dwPG));
        KdPrint(("PAE = 0x%08X
    ",dwPAE));
        KdPrint(("PSE = 0x%08X
    ",dwPSE));
        KdPrint(("Cr0 = 0x%08X
    ",dwCr0));
        KdPrint(("Cr4 = 0x%08X
    ",dwCr4));
    
        //----------------------------------------------------------------------------
        // PE标志位
        if (0 != dwPE){
            RtlStringCchCatNA(
                g_szMemInfo, 
                BUFFERSIZE, 
                "----------------------保护模式(PE=1)-------------------
    ",
                BUFFERSIZE - sizeof("----------------------保护模式(PE=1)-------------------
    "));
        }
        else{
            RtlStringCchCatNA(
                g_szMemInfo,
                BUFFERSIZE ,
                "----------------------实地址模式(PE=0)-------------------
    ",
                BUFFERSIZE - sizeof("----------------------实地址模式(PE=0)-------------------
    "));
        }
    
        //----------------------------------------------------------------------------
        // WP标志位
        if (0 != dwWP){
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "内存写保护(WP)开启...
    "
                );
        }
        else{
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "内存写保护(WP)禁止...
    "
                );
        }
    
        //----------------------------------------------------------------------------
        // PG标志位
        if (0 != dwPG){
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "页机制(PG)启用
    "
                );
        }
        else{
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "页机制(PG)禁止
    "
                );
        }
    
        //----------------------------------------------------------------------------
        // PAE标志位
        if (0 != dwPAE){
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "物理地址扩展(PAE)已开启
    "
                );
        }
        else{
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "物理地址扩展(PAE)未启用
    "
                );
        }
    
        //----------------------------------------------------------------------------
        // PSE标志位
        if (0 != dwPSE){
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "页面大小扩展(PSE)已开启
    "
                );
        }
        else{
            RtlStringCchCatA(
                g_szMemInfo,
                BUFFERSIZE,
                "页面大小扩展(PSE)未启用
    "
                );
        }
    
        KdPrint(("%s
    ", g_szMemInfo));

    最后,看看效果运行图。Demo是在ring3层定义一个Unicoe字符串:“Lthis,然后将其虚拟地址传入ring0层,ring0解析后传出对应的物理地址。

    开启PAE下运行的效果:

    未开启PAE的运行效果:

    附件地址:链接:http://pan.baidu.com/s/1kTENdnL 密码:g5j7

  • 相关阅读:
    有关位域的理解和说明
    LINUX 循环fork()
    关于Linux平台malloc的写时拷贝(延迟分配)【转】
    字符串常量到底存放在哪个存储区[转]
    linux下安装eclipse
    安装和布署项目
    linux 的 samba 实现共享文件夹
    php protobuff 使用
    MFC 配合 protobuff libevent 实现的Socket 的GM工具 框架
    php 学习使用
  • 原文地址:https://www.cnblogs.com/Lthis/p/4746795.html
Copyright © 2020-2023  润新知