18-线性地址转换实验

    xiaoxiao2023-03-24  4

    概述

    上一篇介绍了有关页的概念,以及线性地址转换物理地址过程。受于篇幅限制,抱歉我把实验部分放在了这里。如果你觉得上一篇看完还是晕晕的,那这里的实验希望你能好好做一下。

    实验内容很简单,用 malloc 申请一段内存,然后往这段内存写点数据,顺便查看下申请的这段内存的地址是多少,接着,中断到 WinDbg 中,使用线性地址转换方法,找到这个线性地址对应的物理地址,并验证这段物理地址中保存的数据就是你在程序中写入的。

    实验代码

    // 文件名:LinearAddr.cpp #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char* argv[]) { char buf[] = "hello world! I'm in linear address convertion experiment!"; char *p = (char*)malloc(strlen(buf)+1); strcpy(p, buf); printf("%s\n", p); printf("%08x\n", p); getchar(); free(p); return 0; }

    实验步骤

    编译并运行

    在虚拟机中编译以上代码,运行。这时候会在 getchar()处停下来,然后不要回车,在 WinDbg 中中断下来。

    图1 运行,然后中断到 WinDbg

    转换线性地址

    图1中看到的线性地址是 003729a0,按照10-10-12拆分,得到的值是 0000 0000 00—-11 0111 0010—-1001 1010 0000 即000-372-9a0. 以后,我会把这种拆分后的地址称为三段式结构。

    其中第一段是一级页表索引号,它的值是 0,一级页表英文简称是 PDT第二段是二级页表索引号,它的值是 372(注意这是16进制),二级页表的简称是 PTT。第三段是普通页的页内偏移。

    首先我们需要知道 PDT 的基址。在 WinDbg 中使用 !process 0 0查看。

    图2 使用 !process 0 0 查看DirBase 然后一直找到最后一个位置,LinearAddr.exe 进程。见图3. 图3 进程 LinearAddr.exe 的页目录基址是 19656000

    所以 PDT=0x19656000.

    接下来,就是查找 PDT[0] 的值。因为每个元素大小是4字节,PDT[0] 的值就是地址 PDT + 0*4 处的值。

    图4 查看 PDT[0] 的值

    我们知道,一级页表 PDT 中的每个元素存储了二级页表的编号,那么这里看到的二级页表编号是否就是 1410d067 呢?还记得我说过吗,页表编号只需要用 20 bit 来存储,想起来了吗?

    页表中的每个元素的高20位是用来存储页表编号的,所以这里二级页表的编号应该是 1410d,后面的 067 只是 1410d 号页面的属性。根据运算规则,可以计算出二级页表的基址为 1410d000(因为每个页大小是 4KB)。

    故有 PTT=1410d000.

    然后我们拿着线性地址的第二段,它是一个索引号,它的值是16进制0x372. 所以我们需要去查看 PTT[0x372] 是多少。因为每个元素的大小是4字节,所以PTT[0x372]的值就是地址PTT+0x372*4处的值。

    图5 查看 PTT[372] 的值

    最终我们得到 PTT[0x372]=0736c067,依据规则,我们得到该保存的页面编号的值是 0736c。于是计算得到这个页的页基址是 0736c000,这只是一个普通页,用来保存数据的。

    我们还有线性地址的第 3 段没有使用,它记录了普通页的页内偏移值。所以,最终的物理地址就是 0736c000+9a0 = 0736c9a0

    在WinDbg中使用命令!db 0736c9a0就可以按字节方式查看该地址存储的数据。

    图6 查看最终的物理地址处的数据

    如果最后你能看到右侧有 hello world! I'm in ...的字样,恭喜你,完成了该实验。

    总结

    本篇使用 WinDbg + VM 虚拟机双机调试,实现手工线性地址转换实验,以加深对10-10-12线性地址转换的理解。

    实验中使用了 !process 0 0查看进程的页目录基址,每个进程都有属于自己的页目录基址,当CPU执行某个进程的时候,就会把这个进程的页目录基址加载的 CR3 寄存器,因为每个进程的页目录基址都不一样,所以使用的一套页目录页表也是不同的。这也就是为什么不同进程之间的地址空间是隔离的。

    如果你感兴趣的话,可以尝试着修改一个进程的页表,来修改另一个进程空间地址的值。甚至,你可以读写地址为0的内存单元的值。

    转载请注明原文地址: https://ju.6miu.com/read-1200945.html
    最新回复(0)