地址映射与共享

跟踪地址映射过程

1.通过命令./dbg-asm启动调试器,在linux-0.11运行test.c文件,使其进入死循环,我们的任务就是找到i的地址并将其修改为0使test.c程序退出循环。

2.在命令行输入crit+c使Boch暂停,一般会显示下列信息

(0) [0x00fc8031] 000f:00000031 (unk. ctxt): cmp dword ptr ds:0x3004, 0x00000000 ; 833d0430000000

其中的 000f 如果是 0008,则说明中断在了内核里。那么就要 c,然后再 ctrl+c,直到变为 000f 为止。

如果显示的下一条指令不是 cmp …(这里指语句以 cmp 开头),就用 n 命令单步运行几步,直到停在 cmp …。

3.使用命令 u /8,显示从当前位置开始 8 条指令的反汇编代码,从反汇编代码可以看到,变量i保存在ds:0x3004地址上。

接下来,开始寻找ds:0x3004对应的物理地址

ds:0x3004为虚拟地址,我们首先找到段表,根据ds的值在段表中找到ds段的具体信息。

段表保存在ldtr寄存器中,我们在调试窗口输入sreg命令就会得到

<bochs:4> sreg
cs:s=0x000f, dl=0x00000002, dh=0x10c0fa00, valid=1
ds:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=3
ss:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1
es:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1
fs:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1
gs:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1
ldtr:s=0x0068, dl=0xa2d00068, dh=0x000082fa, valid=1
tr:s=0x0060, dl=0xa2e80068, dh=0x00008bfa, valid=1
gdtr:base=0x00005cb8, limit=0x7ff
idtr:base=0x000054b8, limit=0x7ff

可以看到ldtr的值是0x0068=0000000001101000(二进制,表示LDT表存放在GDT表的1101(二进制)=13(十进制号位置(每位数据的意义参考后文叙述的段选择子)。

GDT的位置已经由gdtr明确给出,在物理地址的0x00005cb8

GDT表中的每一项占 64 位(8 个字节),所以我们要查找的项的地址是0x00005cb8+13*8

输入xp /2w 0x00005cb8+13*8,得到:

<bochs:6> xp /2w 0x00005cb8+13*8
[bochs]:
0x00005d20 <bogus+       0>:    0xa2d00068    0x000082fa

“0xa2d00068 0x000082fa”将其中的加粗数字组合为“0x00faa2d0”,这就是 LDT 表的物理地址(为什么这么组合,参考后文介绍的段描述符)。

xp /8w 0x00faa2d0,得到:

<bochs:7> xp /8w 0x00faa2d0
[bochs]:
0x00faa2d0 <bogus+       0>:    0x00000000    0x00000000    0x00000002    0x10c0fa00
0x00faa2e0 <bogus+      16>:    0x00003fff    0x10c0f300    0x00000000    0x00fab000

这就是LDT表的前四项内容了

ds选择子:保存段在段表中的索引值,用这个索引值可以从段表中选择出相应的段描述符

段选择子的结构为

其中 RPL 是请求特权级,当访问一个段时,处理器要检查 RPL 和 CPL(放在 cs 的位 0 和位 1 中,用来表示当前代码的特权级),即使程序有足够的特权级(CPL)来访问一个段,但如果 RPL(如放在 ds 中,表示请求数据段)的特权级不足,则仍然不能访问,即如果 RPL 的数值大于 CPL(数值越大,权限越小),则用 RPL 的值覆盖 CPL 的值。

而段选择子中的 TI 是表指示标记,如果 TI=0,则表示段描述符(段的详细信息)在 GDT(全局描述符表)中,即去 GDT 中去查;而 TI=1,则去 LDT(局部描述符表)中去查。

通过前面的sreg命令我们知道ds=0x0017,展开为二进制为0000000000010111,所以 RPL=11,可见是在最低的特权级(因为在应用程序中执行),TI=1,表示查找 LDT 表,索引值为 10(二进制)= 2(十进制),表示找 LDT 表中的第 3 个段描述符(从 0 开始编号)

LDT 和 GDT 的结构一样,每项占 8 个字节。所以第 3 项 0x00003fff 0x10c0f300(上一步骤的最后一个输出结果中) 就是搜寻好久的 ds 的段描述符了。

段选择符内容

可以看到,段描述符是一个 64 位二进制的数,存放了段基址和段限长等重要的数据。其中位 P(Present)是段是否存在的标记;位 S 用来表示是系统段描述符(S=0)还是代码或数据段描述符(S=1);四位 TYPE 用来表示段的类型,如数据段、代码段、可读、可写等;DPL 是段的权限,和 CPL、RPL 对应使用;位 G 是粒度,G=0 表示段限长以位为单位,G=1 表示段限长以 4KB 为单位;其他内容就不详细解释了。

费了很大的劲,实际上我们需要的只有段基址一项数据,即段描述符 “0x00003fff 0x10c0f300” 中加粗部分组合成的 “0x10000000”。这就是 ds 段在线性地址空间中的起始地址。用同样的方法也可以算算其它段的基址,都是这个数。

段基址+段内偏移,就是线性地址了。所以 ds:0x3004 的线性地址就是:

0x10000000 + 0x3004 = 0x10003004

calc ds:0x3004命令可以验证这个结果.

找到了虚拟内存的线性地址,接下来就是找对应的物理地址,而实际操作系统管理内存使用的是分页机制,因此我们需要查询页表

前面找到的线性地址为0x10003004

首先需要算出线性地址中的页目录号、页表号和页内偏移,它们分别对应了 32 位线性地址的 10 位 + 10 位 + 12 位,所以 0x10003004 的页目录号是 64,页号 3,页内偏移是 4。

通过creg命令可以查看页目录表的位置,页目录表的位置保存在CR3寄存器中

CR0=0x8000001b: PG cd nw ac wp ne ET TS em MP PE
CR2=page fault laddr=0x10002f68
CR3=0x00000000
    PCD=page-level cache disable=0
    PWT=page-level writes transparent=0
CR4=0x00000000: osxmmexcpt osfxsr pce pge mce pae pse de tsd pvi vme

说明页目录表的基址为0,通过xp/68w 0查看其内容

0x00000000 :    0x00001027    0x00002007    0x00003007    0x00004027
0x00000010 :    0x00000000    0x00024764    0x00000000    0x00000000
0x00000020 :    0x00000000    0x00000000    0x00000000    0x00000000
0x00000030 :    0x00000000    0x00000000    0x00000000    0x00000000
0x00000040 :    0x00ffe027    0x00000000    0x00000000    0x00000000
0x00000050 :    0x00000000    0x00000000    0x00000000    0x00000000
0x00000060 :    0x00000000    0x00000000    0x00000000    0x00000000
0x00000070 :    0x00000000    0x00000000    0x00000000    0x00000000
0x00000080 :    0x00ff3027    0x00000000    0x00000000    0x00000000
0x00000090 :    0x00000000    0x00000000    0x00000000    0x00000000
0x000000a0 :    0x00000000    0x00000000    0x00000000    0x00000000
0x000000b0 :    0x00000000    0x00000000    0x00000000    0x00ffb027
0x000000c0 :    0x00ff6027    0x00000000    0x00000000    0x00000000
0x000000d0 :    0x00000000    0x00000000    0x00000000    0x00000000
0x000000e0 :    0x00000000    0x00000000    0x00000000    0x00000000
0x000000f0 :    0x00000000    0x00000000    0x00000000    0x00ffa027
0x00000100 :    0x00faa027    0x00000000    0x00000000    0x00000000

页目录表和页表中的内容很简单,是 1024 个 32 位(正好是 4K)数。这 32 位中前 20 位是物理页框号,后面是一些属性信息(其中最重要的是最后一位 P)。其中第 65 个页目录项就是我们要找的内容,用“xp /w 0+64*4”查看:

0x00000100 :    0x00faa027

其中的 027 是属性,显然 P=1,其他属性实验者自己分析吧。页表所在物理页框号为 0x00faa,即页表在物理内存的 0x00faa000 位置。从该位置开始查找 3 号页表项,得到(xp /w 0x00faa000+3*4):

0x00faa00c :    0x00fa7067

其中067是属性,显然P=1;

线性地址 0x10003004 对应的物理页框号为 0x00fa7,和页内偏移 0x004 接到一起,得到 0x00fa7004,这就是变量 i 的物理地址。可以通过两种方法验证。

第一种方法是用命令 page 0x10003004,可以得到信息:

linear page 0x10003000 maps to physical page 0x00fa7000

第二种方法是用命令 xp /w 0x00fa7004,可以看到:

0x00fa7004 :    0x12345678

现在,通过直接修改内存来改变 i 的值为 0,命令是: setpmem 0x00fa7004 4 0,表示从 0x00fa7004 地址开始的 4 个字节都设为 0。然后再用“c”命令继续 Bochs 的运行,可以看到 test 退出了,说明 i 的修改成功了,此项实验结束。

总结一下求物理地址的步骤

  • 找出LDT在GDT中的位置

  • 根据ds的值得到段描述符索引,TI

  • 在段表中找段描述符,求出线性地址

  • 根据线性地址查页表

    • 线性地址前十位为页目录项,通过CR3寄存器找到页目录的起始地址,然后根据页目录项找到对应的物理页框
    • 线性地址中间十位为页表号,根据页表号在物理页框的位置找到页表号在物理内存的位置
    • 线性地址最后十二位为业内偏移,由前面得到的页表号在物理内存的位置加上业内偏移得到物理内存

性地址前十位为页目录项,通过CR3寄存器找到页目录的起始地址,然后根据页目录项找到对应的物理页框
* 线性地址中间十位为页表号,根据页表号在物理页框的位置找到页表号在物理内存的位置
* 线性地址最后十二位为业内偏移,由前面得到的页表号在物理内存的位置加上业内偏移得到物理内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值