12

[原创]物理地址、线性地址、逻辑地址之我见

 3 years ago
source link: https://bbs.pediy.com/thread-268246.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
[原创]物理地址、线性地址、逻辑地址之我见-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[基础知识] [原创]物理地址、线性地址、逻辑地址之我见
2天前 768

闲来无事,翻看收藏夹的连接,看到了描述这些地址的文章,看完之后总觉得缺少些什么。思之片刻有感,文章中没有给出简单的证实方法。随后百度之……论坛之……,然而,并未找到想要的结果,反而,产生了新的疑问。在众多文章中对于这些地址的概念描述差异不大,但是,对于范围大小的描述却各有千秋。这在……让我懵逼的同时,也牵动了我较真的神经!因此,本文试图解决以下几个问题:

  • 如何简单的证实这些地址,或者在一些触手可及的工具上找到些许线索证实。
  • 明确这些地址的范围。
  • 理解这些地址的基本作用。

本节虽名为前言,实则是发帖时才写的。下面的内容原本应为笔记,但独享不如分享!如有谬误,还望海涵,且请!多多指正。

文中内容以 Intel CPU 和 Windows 系统为例。

  • Physical address: CPU 可访问的地址空间(如 8086 可访问 20 Bits 地址空间)。物理空间的范围为 36 ≤ MAXPHYADDR ≤ 52 Bits(64 GBytes ~ 4 PBytes),MAXPHYADDR 的值由 CPUID.80000008H:EAX[7:0] 查询。(Pentium Pro 之前皆为 32 Bits)。

  • Linear address: 有时也称 Virtual address。Linear address 是 Physical address 与 Logical address 的中间层,或称隔离层(保护模式隔离用户和资源……个人理解)。空间的范围为 32/48 Bits(4 GBytes/256 TBytes),具体由 CPUID.80000008H:EAX[15:8] 查询(如果 CPUID.80000001H:EDX.LM [29] = 1 则为 48 Bits,否则为 32 Bits。不支持此查询的 CPU 皆为 32 Bits)。

  • Logical address: 程序代码中使用的地址,在不同的编程环境中略有差异,实则已被削弱(见后文)。

mov eax, 0x80000008
cpuid
; Intel I7 6850K:
;         Linear address space  : ah = 48 Bits(支持 256 TBytes)
;         Phyiscal address space: al = 46 Bits(支持  64 TBytes) = MAXPHYADDR

在 Intel 的文档中,同为 6 系列的 CPU 中 MAXPHYADDR 值不同。

物理地址中映射的是什么?答案是各种外部设备。一些地方将物理地址称为内存地址,个人认为亦对亦错,对的原因是 CPU 操作内存最为快捷(似乎也只能操作内存),而物理地址是为了让 CPU 将各种设备都视为内存。错的原因是物理地址中可以映射内存(条),但不局限于此!Intel 的 CPU 有两种物理空间:

  • Memory: 前面描述的大小为 MAXPHYADDR 的物理空间,这个空间中映射了各种外部设备:显卡、声卡、网卡、SATA、USB、APIC 等等。

  • I/O Ports: 独立编址的空间,仅有 64 KBytes(0 ~ 0xFFFF),此空间映射的也是各种外部设备。可通过 in/out 指令访问。0 ~ 255 的端口号使用立即数表示,超过 255 的端口号使用 dx 寄存器表示,存取的数据置于 eax/ax/al 中。

Windows 设备管理器按类型列出资源,其中的[内存]和[输入/输出(I/O)]项,即上述两种物理空间所映射的设备。在 x64 Windows 10 的[内存]项中,物理地址的值范围在 2^32 以内,这也许是设备厂商出于向下(x86)兼容的考虑。

做些简单的测试:

  • 测试一:将虚拟机内存调至 1 GBytes 或更低,然后启动虚拟机查看[内存]项,其中的地址值超过 1 GBytes。这就证实了物理地址……并非内存。
  • 测试二:[内存]项中皆为物理地址,而非线性地址,诸如,0x000A0000 ~ 0x000BFFFF,在本地机和虚拟机中皆为显卡相关,而这块地址也与实模式的显存映射地址一致。这就证实了这里使用的皆为物理地址。

x86/x64 CPU 支持 32/64 Bits 寄存器寻址(4 GBytes ~ 16 EBytes),前者(x86)不会有任何问题,因为线性地址范围最小支持 32 Bits(当然也兼容更古老的 CPU)。然而,后者(x64)却引发了问题:CPU 仅支持 48 Bits 线性地址,因此,厂商规定线性地址的高 16 Bits 被作为符号扩展(0x0000... 或 0xFFFF...),这样的线性地址称为 canonical 地址;反之视为 non-canonical 地址(如高 16 Bits 的 0x0001... 或 0xFFF0... 等等),使用 non-canonical 地址将引发异常(Intel 手册描述)。

CPU 线性地址布局:

  • 0x00000000`00000000 ~ 0x00007FFF`FFFFFFFF:canonical 地址共 128 TBytes。
  • 0xFFFF8000`00000000 ~ 0xFFFFFFFF`FFFFFFFF:canonical 地址共 128 TBytes。
  • 0x00008000`00000000 ~ 0xFFFF7FFF`FFFFFFFF:non-canonical 地址共 16,776,960 TBytes。

    合法线性地址的 Bit 47 必须和 Bit 48 ~ 63 一致,否则为非法地址。若以后需要更大的空间,只需将符号位上移。

从 x64 Windows 8.1 及其之后的版本线性地址支持 256 TBytes,这与 CPU 描述的线性地址吻合。而之前的版本仅支持 16 TBytes 线性地址(微软描述,实测为 248 TBytes)。

SYSTEM_INFO info = {};
GetSystemInfo (&info);
/*
x64 Win7:
info.lpMinimumApplicationAddress = 0x00000000`00010000
info.lpMaximumApplicationAddress = 0x000007FF`FFFEFFFF
x64 Win10:
info.lpMinimumApplicationAddress = 0x00000000`00010000
info.lpMaximumApplicationAddress = 0x00007FFF`FFFEFFFF
*/

可以通过 windbg 的 !address 命令枚举线性地址布局,还可以使用 !pte 命令查看线性地址是否 canonical:

x64 Win7:

kd>!address
....
BaseAddress      EndAddress+1        RegionSize         VaType
----------------------------------------------------------------------------
0`00000000        0`00010000        0`00010000         UserRange                          
....       
7FF`FFFF0000      800`00000000        0`00010000         UserProbeArea                      
800`00000000     8000`00000000     7800`00000000         <unknown>                          
8000`00000000 FFFF8000`00000000 FFFF0000`00000000         NonAddressable                     
FFFF0800`00000000 FFFF8000`00000000     7800`00000000         SystemRange                        
FFFF8000`00000000 FFFFF680`00000000     7680`00000000         SystemRange                        
FFFFF680`00000000 FFFFF700`00000000       80`00000000         PageTables                         
....
FFFFFFFF`FFC00000 FFFFFFFF`FFFFFFFF        0`00400000         HAL

用户模式线性地址空间是固定大小的:0x800`00000000(因从 0 计数,有效地址要 - 1) = 8 TBytes。未知区域(unknown)大小 = 0x7800`00000000 = 120 TBytes。

内核模式从高到低对地址进行布局:HAL - PageTables + 1 = 0xFFFFFFFF`FFFFFFFF - 0xFFFFF680`00000000 + 1 = 0x980`00000000 = 9.5 TBytes。

从上向下数第一块 SystemRange 的 0xFFFF0800`00000000 和 0xFFFF8000`00000000 - 1 这两个地址都是 non-canonical,但如果将这两个地址的高 16 Bits 清零,则与 unknown 吻合(应该是微软将用户模式没有使用的 120 TBytes 归为内核了)。

第二块 SystemRange 的大小 = 118.5 TBytes(128 - 9.5)。综上内核模式共占 248 TBytes(0xFFFFFFFF`FFFFFFFF - 0xFFFF0800`00000000 + 1)。

x64 Win10:

kd>!address
....
BaseAddress      EndAddress+1        RegionSize         VaType
----------------------------------------------------------------------------
0`00000000        0`7ffe0000        0`7ffe0000         UserRange                          
....       
7fff`ffff0000 ffff8000`00000000 ffff0000`00010000         UserProbeArea                      
8000`00000000 ffff8000`00000000 ffff0000`00000000         NonAddressable                     
ffff8000`00000000 ffffa908`ac201000     2908`ac201000         SystemRange
....       
ffffffff`ffc00000 ffffffff`ffffffff        0`00400000         HAL

标准的 canonical 地址,用户与内核模式各占 128 TBytes。(SystemRange 块的 EndAddress 的值是随机的……不明所以)。

实模式,或保护模式分段下,线性地址就是物理地址;保护模式分页下,线性地址转物理地址涉及 32/48 Bits 的两种分页模式,论坛中有其他文章介绍,因此,就不赘言了。

言不如表;表不如图。下图截取自 Intel 手册,图中绘出了逻辑地址到线性地址的转换过程。从图中大致可以将逻辑地址归纳为 'Segment:Offset' 形式,通常 Segment 是隐式的。而现今的编程环境基本皆为 Flat Model,也就是说逻辑地址已经被削弱,取而代之的是线性地址,至少 c/c++ 中指针使用线性地址。

Memory Limits for Windows and Windows Server Releases

Kernel Virtual Address Layout

I/O Ports

[注意] 招人!base上海,课程运营、市场多个坑位等你投递!

最后于 2天前 被khristian编辑 ,原因:

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK