PostgreSQL 原始碼解讀(226)- Linux Kernel(虛擬記憶體)

husthxd發表於2019-09-11

PostgreSQL使用程式架構,每個連線對應一個後臺程式,為了更好的理解這種架構,有必要深入理解程式的相關知識.本節主要介紹了Linux下的程式虛擬記憶體結構.

一、虛擬記憶體

虛擬記憶體是一種通過軟硬體結合實現的記憶體管理機制.程式通過記憶體對映表建立虛擬記憶體地址和實體記憶體地址之間的對映關係.在程式或任務看起來儲存就像是連續的地址空間,或者是連續段的集合.OS管理虛擬地址空間和實際記憶體->虛擬記憶體的分配.在CPU中的地址轉換硬體通常稱為記憶體管理單元(簡稱MMU),自動轉換虛擬地址為實體地址.OS中軟體可以擴充套件這些機制來提供比實際記憶體大多的虛擬地址空間.
使用虛擬記憶體的優點包括通過強制管理共享記憶體空間來釋放應用佔用記憶體/通過記憶體隔離增強安全性/可使用比實際記憶體要大的記憶體空間/使用頁技術等.
虛擬記憶體

每個程式都有自己的虛擬記憶體空間:
1.虛擬記憶體的大小取決於系統架構;
2.OS處理虛擬記憶體的方式不太一樣,Linux下,虛擬記憶體分為核心空間和使用者空間兩部分.
程式的虛擬記憶體結構

程式的虛擬記憶體空間佈局

1.只讀段,包括程式碼和常量等;
2.資料段,包括全域性變數等;
3.堆,包括動態分配的記憶體,從低地址開始向上增長;
4.檔案對映段,包括動態庫/共享記憶體等,從高地址開始向下增長;
5.棧,包括區域性變數和函式呼叫的上下文等.

二、proc檔案系統

下面先看一個C語言的例子:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
 * main - uses strdup to create a new string, and prints the
 * address of the new duplcated string
 *
 * Return: EXIT_FAILURE if malloc failed. Otherwise EXIT_SUCCESS
 */
int main(void)
{
    char *s;
    s = strdup("Holberton");
    if (s == NULL)
    {
        fprintf(stderr, "Can't allocate mem with malloc\n");
        return (EXIT_FAILURE);
    }
    printf("%p\n", (void *)s);
    return (EXIT_SUCCESS);
}

原始碼另存為main.c檔案,編譯執行

[root@localhost linux]# gcc -Wall -Wextra -pedantic -Werror main.c -o holberton
[root@localhost linux]# ./holberton 
0x1bc2010

輸出的0x1bc2010是字串在程式虛擬記憶體空間中的地址.
實際上,Linux通過/proc/[pid]/maps來儲存程式對映的虛擬記憶體區間和許可權資訊.

A file containing the currently mapped memory regions and their access permissions.

/proc/[pid]/mem檔案用於open/read/lseek函式訪問程式(虛擬)記憶體頁.

/proc/[pid]/mem
This file can be used to access the pages of a process’s memory through open(2), read(2), and lseek(2).

下面我們稍微修改下main.c中的邏輯,每隔1s列印字串

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/**              
 * main - uses strdup to create a new string, loops forever-ever
 *                
 * Return: EXIT_FAILURE if malloc failed. Other never returns
 */
int main(void)
{
     char *s;
     unsigned long int i;
     s = strdup("Holberton");
     if (s == NULL)
     {
          fprintf(stderr, "Can't allocate mem with malloc\n");
          return (EXIT_FAILURE);
     }
     i = 0;
     while (s)
     {
          printf("[%lu] %s (%p)\n", i, s, (void *)s);
          sleep(1);
          i++;
     }
     return (EXIT_SUCCESS);
}

編譯執行,輸出如下

[root@localhost linux]# gcc -Wall -Wextra -pedantic -Werror loop.c -o loop
[root@localhost linux]# ./loop
[0] Holberton (0x1db0010)
...

下面分析程式loop在/proc中的資訊
首先,獲取該程式的pid

[root@localhost linux]# ps -aux|grep './loop'
root     21437  0.0  0.0   4300   348 pts/1    S+   16:07   0:00 ./loop

檢視該程式的maps和mem

[root@localhost linux]# cat /proc/21437/maps 
00400000-00401000 r-xp 00000000 fd:00 251955270                          /data/source/linux/loop
00600000-00601000 r--p 00000000 fd:00 251955270                          /data/source/linux/loop
00601000-00602000 rw-p 00001000 fd:00 251955270                          /data/source/linux/loop
01db0000-01dd1000 rw-p 00000000 00:00 0                                  [heap]
7f605d2e0000-7f605d498000 r-xp 00000000 fd:00 153635                     /usr/lib64/libc-2.17.so
7f605d498000-7f605d698000 ---p 001b8000 fd:00 153635                     /usr/lib64/libc-2.17.so
7f605d698000-7f605d69c000 r--p 001b8000 fd:00 153635                     /usr/lib64/libc-2.17.so
7f605d69c000-7f605d69e000 rw-p 001bc000 fd:00 153635                     /usr/lib64/libc-2.17.so
7f605d69e000-7f605d6a3000 rw-p 00000000 00:00 0 
7f605d6a3000-7f605d6c4000 r-xp 00000000 fd:00 153628                     /usr/lib64/ld-2.17.so
7f605d8b7000-7f605d8ba000 rw-p 00000000 00:00 0 
7f605d8c2000-7f605d8c4000 rw-p 00000000 00:00 0 
7f605d8c4000-7f605d8c5000 r--p 00021000 fd:00 153628                     /usr/lib64/ld-2.17.so
7f605d8c5000-7f605d8c6000 rw-p 00022000 fd:00 153628                     /usr/lib64/ld-2.17.so
7f605d8c6000-7f605d8c7000 rw-p 00000000 00:00 0 
7ffd04019000-7ffd0403a000 rw-p 00000000 00:00 0                          [stack]
7ffd0404f000-7ffd04051000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
[root@localhost linux]# 
[root@localhost linux]# ll /proc/21437/mem
-rw-------. 1 root root 0 Sep 11 16:09 /proc/21437/mem

程式loop的堆區間為01db0000-01dd1000,許可權為rw-p(read/write/private)

r = read
w = write
x = execute
s = shared
p = private (copy on write)

./loop輸出的地址為0x1db0010,為字串分配的記憶體空間位於堆空間中.已知字串的地址和大小,可以通過待修改虛擬記憶體中的字串值,Hack程式輸出.
執行hack

[root@localhost linux]# python3.4 read_write_heap.py 21437 Holberton "Hi,Hacker"
[*] maps: /proc/21437/maps
[*] mem: /proc/21437/mem
[*] Found [heap]:
    pathname = [heap]
    addresses = 01db0000-01dd1000
    permisions = rw-p
    offset = 00000000
    inode = 0
    Addr start [1db0000] | end [1dd1000]
[*] Found 'Holberton' at 10
[*] Writing 'Hi,Hacker' at 1db0010
[root@localhost linux]#

hack後的程式輸出

...
[719] Holberton (0x1db0010)
[720] Hi,Hacker (0x1db0010)
[721] Hi,Hacker (0x1db0010)
[722] Hi,Hacker (0x1db0010)
[723] Hi,Hacker (0x1db0010)
[724] Hi,Hacker (0x1db0010)
[725] Hi,Hacker (0x1db0010)
[726] Hi,Hacker (0x1db0010)
[727] Hi,Hacker (0x1db0010)
[728] Hi,Hacker (0x1db0010)
[729] Hi,Hacker (0x1db0010)
[730] Hi,Hacker (0x1db0010)

三、參考資料

Virtual memory
Hack The Virtual Memory: C strings & /proc

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2656764/,如需轉載,請註明出處,否則將追究法律責任。

相關文章