【轉】內存尋址原理


原文:http://blog.nsfocus.net/memory-addressing-mode/

 

內存尋址原理
9.png
在做網絡安全事件分析的時候,都會遇到內存尋址的知識,例如上次跟大家分享的《 空指針漏洞防護技術》,就涉及到非法訪問內存地址的問題。如果這個坎兒邁不過去,你就會迷失在代碼中,更無從分析了。今天綠盟科技的安全技術專家就講講這個內存尋址的原理,文章分為上下兩篇《內存尋址原理》及《內存尋址方式》。

隨着信息化發展和數據處理能力需求的提高,對計算機硬件產品的性能和容量也提出了新的挑戰,要求計算機處理能力也要能隨實際情況需求的變動而提升、改變。

當下,一台普通的電腦硬盤容量也要200多G,內存也有4G;如此大容量的硬盤和內存,在處理大量數據或是大型游戲面前還是顯得力不從心,需要通過擴容來滿足需求,比如將內存由4G提升到8G或是16G不等。擴容后對個人體驗確實提升不少。對於內存容量的提升需要有相應的硬件基礎支撐,需要有能消化掉這么多內存的尋址地址。比如說如果一8位單片機如果要裝載16G的內存,那就是暴殄天物。

哪里有需求哪里就有市場 ;計算機從8位的51單片機,20位8086尋址,發展到32位 win2003,64位win10,都是由於信息化需求的膨脹推動着計算機一代又一代的改革創新。

對於內存的擴容,很多人都不是很清楚應用程序如何使用的物理內存地址。遠了不說,單說現在常用計算機中的32位、64系統;系統是怎么樣將虛擬地址轉化成線性地址,線性地址又是怎樣轉換成物理地址的,其中又用到了哪些寄存器或是數據結構,相信很多人對此也是一知半解;也像我一樣,想結合實例從地址轉換的本質來掌握其精髓之處。接下來就一起學習從邏輯地址到物理地址的整個轉換過程。

1.實模式與保護模式簡介

CPU常見三種工作模式:實模式與保護模式,虛擬8086模式。

實模式 :CPU復位(reset)或加電(power on)的時候以實模式啟動,處理器以實模式工作。在實模式下,內存尋址方式和8086相同,由16位段寄存器的內容乘以16(10H)當做段基地址,加上 16位偏移地址形成20位的物理地址,最大尋址空間1MB。在實模式下,所有的段都是可以讀、寫和可執行的。實模式下沒有分段或是分頁機制,邏輯地址和物 理地址相等。

由此得知:

  1. 在實模式下最大尋址空間時1M,1M以上的內存空間在實模式下不會被使用。
  2. 在實模式所有的內存數據都可以被訪問。不存在用戶態、內核態之分。
  3. 在BIOS加載、MBR、ntdlr啟動階段都處在實模式下。

保護模式 :對於保護模式大家並不陌生;是目前操作系統的運行模式,利用內存管理機制來實現線性地址到物理地址的轉換,具有完善的任務保護機制。

保護模式常識:

  1. 現在應用程序運行的模式均處於保護模式。
  2. 橫向保護,又叫任務間保護,多任務操作系統中,一個任務不能破壞另一個任務的代碼,這是通過內存分頁以及不同任務的內存頁映射到不同物理內存上來實現的。
  3. 縱向保護,又叫任務內保護,系統代碼與應用程序代碼雖處於同一地址空間,但系統代碼具有高優先級,應用程序代碼處於低優先級,規定只能高優先級代碼訪問低優先級代碼,這樣杜絕用戶代碼破壞系統代碼。

虛擬8086 模式: 簡稱V86模式是運行在保護模式中的實模式,為了在32位保護模式下執行純16位程序。可以把8086程序當做保護模式的一項任務來執行。虛擬8086允許在不退出保護模式的情況下執行8086程序。

虛擬8086常識:

  1. 尋址的地址空間是1M字節.
  2. 可以在虛擬8086模式下運行16位DOS程序。
  3. 在V86模式下,代碼段總是可寫的;這與實模式相同,同理,數據段也是可執行的。
  4. 32系統編寫V86模式的程序

12.png

2. 保護模式尋址基礎知識

接下來就以32位系統為例,介紹保護模式下,內存中一些地址轉換相關的寄存機和數據結構。

2.1 內存地址概念

邏輯地址 :在進行C語言編程中,能讀取變量地址值(&操作),實際上這個值就是邏輯地址,也可以是通過malloc或是new調用返回的地址。該地址是相 對於當前進程數據段的地址,不和絕對物理地址相干。只有在Intel實模式下,邏輯地址才和物理地址相等(因為實模式沒有分段或分頁機制,CPU不進行自 動地址轉換)。應用程序員僅需和邏輯地址打交道,而分段和分頁機制對一般程序員來說是完全透明的,僅由系統編程人員涉及。應用程序員雖然自己能直接操作內 存,那也只能在操作系統給你分配的內存段操作。一個邏輯地址,是由一個段標識符加上一個指定段內相對地址的偏移量,表示為 [段標識符:段內偏移量]。

線性地址 :是邏輯地址到物理地址變換之間的中間層。程序代碼會產生邏輯地址,或說是段中的偏移地址,加上相應段的基地址就生成了一個線性地址。如果啟用了分頁機 制,那么線性地址能再經變換以產生一個物理地址。若沒有啟用分頁機制,那么線性地址直接就是物理地址。Intel 80386的線性地址空間容量為4G(2的32次方即32根地址總線尋址)。

物理地址(Physical Address) 是指出目前CPU外部地址總線上的尋址物理內存的地址信號,是地址變換的最終結果地址。如果啟用了分頁機制,那么線性地址會使用頁目錄和頁表中的項變換成物理地址。如果沒有啟用分頁機制,那么線性地址就直接成為物理地址了,比如在實模式下。

2.2 虛擬地址,線性地址,物理地址關系

對於保護模式下地址之間的轉換,對程序員來說是透明的。那么物理內存通過內存管理機制是如何將虛擬地址轉換為物理地址的呢?當程序中的指令訪問某一 個邏輯地址時,CPU首先會根據段寄存器的內容將虛擬地址轉化為線性地址。如果CPU發現包含該線性地址的內存頁不在物理內存中就會產生缺頁異常,該異常 的處理程序通過是操作系統的內存管理器例程。內存管理器得到異常報告后會根據異常的狀態信息。特別是CR2寄存器中包含的線性地址,將需要的內存頁加載到 物理內存中。然后異常處理程序返回使處理器重新執行導致頁錯誤異常的指令,這時所需要的內存頁已經在物理內存中,所以便不會再導致頁錯誤異常。

2.3 段式機制及實例分析

前面說到在線性地址轉換為物理地址之前,要先由邏輯地址轉換為線性地址。系統采用段式管理機制來實現邏輯地址到線性地址的轉換。保護模式下,通過”段選擇符+段內偏移”尋址最終的線性地址。

CPU的段機制提供一種手段可以將系統的內存空間划分為一個個較小的受保護的區域,每個區域為一個段。相對32位系統,也就是把4G的邏輯地址空間換分成不同的段。每個段都有自己的起始地址(基地址),邊界和訪問權限等屬性。實現段機制的一個重要數據結構就是段描述符。

下面是個程序實例中顯示除了各個段的值:

22.png

圖中給出了代碼段CS,堆棧段SS,數據段DS等段寄存器的值;從得到的值可知,SS=DS=ES是相等的,至於為什么有些段的值相等,后面會說到。 以實例中給出的地址0x83e84110 為例,哪里是段描述符,哪里是段內偏移, 又是如何將該邏輯地址轉換為線性地址的呢?相信很多人都迫不及待的想知道整個轉換過程,接下來就要看看邏輯地址到線性地址詳細轉換過程。

上面說到段式管理模式下有段選擇符+段內偏移尋址定位線性地址,其實際轉換過程如下圖所示

32.png

從圖中可知,邏輯地址到線性地址的轉換,先是通過段選擇符從描述符表中找到段描述符,把段描述符和偏移地址相加得到線性地址。也就是說要想得到段描述符需要三個條件:

  1. 得到段選擇符。
  2. 得到段描述符表
  3. 從段描述符表中找到段描述符的索引定位段描述符。

前面我們提到了段描述符 + 偏移地址,並沒有提段選擇符和段描述符表。所以我們要弄清楚這幾個觀念段選擇符,段描述符表,段描述符,以及如何才能得到這幾個描述符?

2.3.1 段描述符基礎知識

從上圖可知,通過段選擇符要通過段描述符表找到段描述符,那么段描述符表是什么,又是怎么得到段描述符表呢?

在保護模式下,每個內存段就是一個段描述符。其結構如下圖所示:

4.png

圖中看出,一個段描述符是一個8字節長的數據結構,用來描述一個段的位置、大小、訪問控制和狀態等信息。段描述符最基本內容是段基址和邊界。段基址 以4字節表示(圖中可看出3,4,5,8字節)。4字節剛好表示4G線性地址的任意地址(0x00000000-0xffffffff)。段邊界20位表 示(1,2字節及7字節的低四位)。

2.3.2 段描述符表實例解析

在現在多任務系統中,通常會同時存在多個任務,每個任務會有多個段,每個段需要一個段描述符,段描述符在上面一小節已經介紹,因此系統中會有很多段 描述符。為了便於管理,需要將描述符保存於段描述符表中,也就是上圖畫出的段描述符表。IA-32處理器有3中描述符表:GDT,LDT和IDT。

GDT是全局描述符表。一個系統通常只有一個GDT表。GDT表也即是上圖中的段描述符表,供系統中所以程序和任務使用。至於LDT和IDT今天不是重點。

那么如何找到GDT表的存放位置呢?系統中提供了GDTR寄存器用了表示GDT表的位置和邊界,也就是系統是通過GDTR寄存器找到GDT表的;在 32位模式下,其長度是48位,高32位是基地址,低16位是邊界;在IA-32e模式下,長度是80位,高64位基地址,低16位邊界。

位於GDT表中的第一個表項(0號)的描述符保留不用,成為空描述符。如何查看系統的GDT表位置呢?通過查看GDTR寄存器,如下圖所示

5.png

從上圖看出GDT表位置地址是0x8095000,gdtl值看出GDT邊界1023,總長度1024字節。前面知道每一項段描述符占8字節。所以總共128個表項。圖中第一表項是空描述符。

2.3.3 段選擇符結構

前面我們介紹了段描述符表和段描述符的格式結構。那么如何通過段選擇符找到段描述符呢,段選擇符又是什么呢?

段選擇符又叫段選擇子,是用來在段描述符表中定位需要的段描述符。段選擇子格式如下:

6.png

段選擇子占有16位兩個字節,其中高13位是段描述在段描述表中的索引。低3位是一些其他的屬性,這里不多介紹。使用13位地址,意味着最多可以索引8k=8192個描述符。但是我們知道了上節GDT最多128個表項。

在保護模式下所有的段寄存器(CS,DS,ES,FS,GS)中存放的都是段選擇子。

2.3.4 邏輯地址到線性地址轉換實例解析

已經了解了邏輯地址到虛擬地址到線性地址的轉換流程,那就看看在前面圖中邏輯地址0x83e84110對應的線性地址是多少?

首先,地址0x83e84110對應的是代碼段的一個邏輯地址,地址偏移已經知道,也就是段內偏移知道,通過寄存器EIP得到是 0x83e34110。段選擇符是CS寄存器CS=0008,其高13位對應的GDT表的索引是1,也就是第二項段描述符(第一項是空描述符)。GDT表 的第二項為標紅的8個字節

7.png

通過段描述的3,4,5,8個字節得到段基址。

8.png

如上圖所示第二項段描述符的3,4,5,8字節對應的值為0x00000000。由此我們得到了段機制和段內偏移。最后的線性地址為段基址+段內偏移=0x0+0x83e34110=0x83e34110。

由此我們知道在32系統中邏輯地址就是線性地址。

其實通過觀察其他的段選擇子會發現,所有段選擇子對應的基地址都是0x0,這是因為在32系統保護模式下,使用了平坦內存模型,所用的基地址和邊界值都一樣。既然基地址都是0,那么也就是線性地址就等於段內偏移=邏輯地址。

總之:

  1. 段描述符8字節
  2. GDTR是48位
  3. 段選擇子2個字節。

2.4 頁式機制及實例分析

前面介紹了由邏輯地址到線性地址的轉換過程,那么接下來就要說說地址是如何將線性地址轉為物理地址。需要先了解一些相關的數據結構。

前面說到如果CPU發現包含該線性地址的內存頁不在物理內存中就會產生缺頁異常,該異常的處理程序通過是操作系統的內存管理器例程。內存管理器得到 異常報告后會根據異常的狀態信息。特別是CR2寄存器中包含的線性地址,將需要的內存頁加載到物理內存中。然后異常處理處理返回使處理器重新執行導致頁錯 誤異常的指令,這時所需要的內存頁已經在物理內存中,所以便不會再導致也錯誤異常。

32位系統中通過頁式管理機制實現線性地址到物理地址的轉換,如下圖:

9.png

2.4.1 PDE結構及如何查找內存頁目錄

從上圖中我們知道通過寄存器CR3可以找到頁目錄表。那么CR3又是什么呢?在32系統中CR3存放的頁目錄的起始地址。CR3寄存器又稱為頁目錄 基址寄存器。32位系統中不同應用程序中4G線性地址對物理地址的映射不同,每個應用程序中CR3寄存器也不同。也就是說每個應用程序中頁目錄基址也是不 同的。

從上圖知道頁目錄表用來存放頁目錄表項(PDE),頁目錄占一個4kb內存頁,每個PDE長度為4字節,所以頁目錄最多包含1KB。沒啟用PAE時,有兩種PDE,這里我們只討論使用常見的指向4KB頁表的PDE。

10.png

頁目錄表項的高20位表示該PDE所指向的起始物理地址的高20位,該起始地址的低12位為0,也就是通過PDE高20位找到頁表。由於頁表低12位0,所以頁表一定是4KB邊界對齊。 也就是通過頁目錄表中的頁目錄表項來定位使用哪個頁表(每一個應用程序有很多頁表)。

以啟動的calc程序為例,CR3寄存器是DirBase中的值,如下圖

111.png

Calc.exe程序對應的CR3寄存器值為0x2960a000,下面是對應PDT結構

2.4.2頁表結構解析

頁表是用來存放頁表表項(PTE)。每一個頁表占4KB的內存頁,每個PTE占4個字節。所以每個頁表最多1024個PTE。其中高20位代表要使用的最終頁面的起始物理地址的高20位。所以4KB的內存頁也都是4KB邊界對齊。

121.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM